diff options
Diffstat (limited to 'Swift')
-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,139 +1,157 @@ #include "Swift/Controllers/ChatsManager.h" +#include <boost/bind.hpp> + #include "Swiften/Client/Client.h" #include "Swift/Controllers/ChatController.h" #include "Swift/Controllers/EventController.h" #include "Swift/Controllers/MUCController.h" #include "Swiften/Presence/PresenceSender.h" namespace Swift { typedef std::pair<JID, ChatController*> JIDChatControllerPair; typedef std::pair<JID, MUCController*> JIDMUCControllerPair; ChatsManager::ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, TreeWidgetFactory* treeWidgetFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, boost::shared_ptr<DiscoInfo> serverDiscoInfo, PresenceSender* presenceSender) : jid_(jid) { eventController_ = eventController; stanzaChannel_ = stanzaChannel; iqRouter_ = iqRouter; chatWindowFactory_ = chatWindowFactory; treeWidgetFactory_ = treeWidgetFactory; nickResolver_ = nickResolver; presenceOracle_ = presenceOracle; avatarManager_ = NULL; serverDiscoInfo_ = serverDiscoInfo; presenceSender_ = presenceSender; + presenceOracle_->onPresenceChange.connect(boost::bind(&ChatsManager::handlePresenceChange, this, _1, _2)); } ChatsManager::~ChatsManager() { foreach (JIDChatControllerPair controllerPair, chatControllers_) { delete controllerPair.second; } foreach (JIDMUCControllerPair controllerPair, mucControllers_) { delete controllerPair.second; } } +/** + * 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; } // void ChatsManager::handleUIEvent(boost::shared_ptr<UIEvent> rawEvent) { // { // boost::shared_ptr<RequestChatUIEvent> event = boost::dynamic_pointer_cast<RequestChatUIEvent>(rawEvent); // if (event != NULL) { // handleChatRequest(event->getContact()); // return; // } // } // { // boost::shared_ptr<JoinMUCUIEvent> event = boost::dynamic_pointer_cast<JoinMUCUIEvent>(rawEvent); // if (event != NULL) { // handleJoinMUCRequest(event->getRoom(), event->getNick()); // } // } // } void ChatsManager::setServerDiscoInfo(boost::shared_ptr<DiscoInfo> info) { foreach (JIDChatControllerPair pair, chatControllers_) { pair.second->setAvailableServerFeatures(info); } foreach (JIDMUCControllerPair pair, mucControllers_) { pair.second->setAvailableServerFeatures(info); } } void ChatsManager::setEnabled(bool enabled) { foreach (JIDChatControllerPair controllerPair, chatControllers_) { //printf("Setting enabled on %d to %d\n", controllerPair.second, enabled); controllerPair.second->setEnabled(enabled); } foreach (JIDMUCControllerPair controllerPair, mucControllers_) { controllerPair.second->setEnabled(enabled); } } void ChatsManager::handleChatRequest(const String &contact) { ChatController* controller = getChatController(JID(contact)); controller->showChatWindow(); controller->activateChatWindow(); } 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); } void ChatsManager::handleJoinMUCRequest(const JID &muc, const String &nick) { mucControllers_[muc] = new MUCController(jid_, muc, nick, stanzaChannel_, presenceSender_, iqRouter_, chatWindowFactory_, treeWidgetFactory_, presenceOracle_, avatarManager_); mucControllers_[muc]->setAvailableServerFeatures(serverDiscoInfo_); } void ChatsManager::handleIncomingMessage(boost::shared_ptr<Message> message) { JID jid = message->getFrom(); boost::shared_ptr<MessageEvent> event(new MessageEvent(message)); if (!event->isReadable()) { return; } // Try to deliver it to a MUC if (message->getType() == Message::Groupchat || message->getType() == Message::Error) { std::map<JID, MUCController*>::iterator i = mucControllers_.find(jid.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; } } //if not a mucroom eventController_->handleIncomingEvent(event); getChatController(jid)->handleIncomingMessage(event); } bool ChatsManager::isMUC(const JID& jid) const { return mucControllers_.find(jid.toBare()) != mucControllers_.end(); } } 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 @@ -1,54 +1,56 @@ #pragma once #include <map> #include <boost/shared_ptr.hpp> #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" namespace Swift { class EventController; class ChatController; class MUCController; class ChatWindowFactory; class TreeWidgetFactory; class NickResolver; class PresenceOracle; class AvatarManager; class StanzaChannel; class IQRouter; class PresenceSender; class ChatsManager : public MUCRegistry { public: ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, TreeWidgetFactory* treeWidgetFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, boost::shared_ptr<DiscoInfo> serverDiscoInfo, PresenceSender* presenceSender); ~ChatsManager(); void setAvatarManager(AvatarManager* avatarManager); void setEnabled(bool enabled); void setServerDiscoInfo(boost::shared_ptr<DiscoInfo> info); void handleIncomingMessage(boost::shared_ptr<Message> message); 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; std::map<JID, MUCController*> mucControllers_; std::map<JID, ChatController*> chatControllers_; EventController* eventController_; JID jid_; StanzaChannel* stanzaChannel_; IQRouter* iqRouter_;; ChatWindowFactory* chatWindowFactory_; TreeWidgetFactory* treeWidgetFactory_; NickResolver* nickResolver_; PresenceOracle* presenceOracle_; AvatarManager* avatarManager_; PresenceSender* presenceSender_; boost::shared_ptr<DiscoInfo> serverDiscoInfo_; }; } 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 @@ -1,144 +1,269 @@ #include <cppunit/extensions/HelperMacros.h> #include <cppunit/extensions/TestFactoryRegistry.h> #include "3rdParty/hippomocks.h" #include "Swift/Controllers/ChatsManager.h" #include "Swift/Controllers/ChatWindow.h" #include "Swift/Controllers/ChatWindowFactory.h" #include "Swiften/Roster/TreeWidgetFactory.h" #include "Swiften/Client/Client.h" #include "Swift/Controllers/ChatController.h" #include "Swift/Controllers/EventController.h" #include "Swift/Controllers/MUCController.h" #include "Swiften/Presence/PresenceSender.h" #include "Swiften/Avatars/UnitTest/MockAvatarManager.h" #include "Swift/Controllers/NickResolver.h" #include "Swiften/Roster/XMPPRoster.h" #include "Swift/Controllers/UnitTest/MockChatWindow.h" #include "Swiften/Client/DummyStanzaChannel.h" #include "Swiften/Queries/DummyIQChannel.h" #include "Swiften/Presence/PresenceOracle.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_SUITE_END(); public: ChatsManagerTest() {}; void setUp() { mocks_ = new MockRepository(); jid_ = JID("test@test.com/resource"); stanzaChannel_ = new DummyStanzaChannel(); iqChannel_ = new DummyIQChannel(); iqRouter_ = new IQRouter(iqChannel_); eventController_ = new EventController(); chatWindowFactory_ = mocks_->InterfaceMock<ChatWindowFactory>(); treeWidgetFactory_ = NULL; xmppRoster_ = boost::shared_ptr<XMPPRoster>(new XMPPRoster()); nickResolver_ = new NickResolver(xmppRoster_); presenceOracle_ = new PresenceOracle(stanzaChannel_); serverDiscoInfo_ = boost::shared_ptr<DiscoInfo>(new DiscoInfo()); presenceSender_ = NULL; manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, treeWidgetFactory_, nickResolver_, presenceOracle_, serverDiscoInfo_, presenceSender_); avatarManager_ = new MockAvatarManager(); manager_->setAvatarManager(avatarManager_); }; void tearDown() { delete manager_; delete presenceSender_; delete avatarManager_; delete presenceOracle_; delete nickResolver_; delete treeWidgetFactory_; delete stanzaChannel_; delete iqChannel_; delete iqRouter_; delete mocks_; } 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() { String messageJIDString("testling@test.com"); ChatWindow* window = new MockChatWindow();//mocks_->InterfaceMock<ChatWindow>(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(messageJIDString)).Return(window); manager_->handleChatRequest(messageJIDString); } void testFirstOpenWindowBareToFull() { String bareJIDString("testling@test.com"); String fullJIDString("testling@test.com/resource1"); MockChatWindow* window = new MockChatWindow();//mocks_->InterfaceMock<ChatWindow>(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(bareJIDString)).Return(window); manager_->handleChatRequest(bareJIDString); 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_; ChatsManager* manager_; StanzaChannel* stanzaChannel_; IQChannel* iqChannel_; IQRouter* iqRouter_; EventController* eventController_; ChatWindowFactory* chatWindowFactory_; TreeWidgetFactory* treeWidgetFactory_; NickResolver* nickResolver_; PresenceOracle* presenceOracle_; AvatarManager* avatarManager_; boost::shared_ptr<DiscoInfo> serverDiscoInfo_; boost::shared_ptr<XMPPRoster> xmppRoster_; PresenceSender* presenceSender_; MockRepository* mocks_; }; CPPUNIT_TEST_SUITE_REGISTRATION(ChatsManagerTest); 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 @@ -1,36 +1,37 @@ #pragma once #include "Swift/Controllers/ChatWindow.h" namespace Swift { class MockChatWindow : public ChatWindow { public: 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*/) {}; virtual void setName(const String& name) {name_ = name;}; virtual void show() {}; virtual void activate() {}; virtual void setAvailableSecurityLabels(const std::vector<SecurityLabel>& labels) {labels_ = labels;}; virtual void setSecurityLabelsEnabled(bool enabled) {labelsEnabled_ = enabled;}; virtual void setUnreadMessageCount(int /*count*/) {}; virtual void convertToMUC() {}; virtual TreeWidget *getTreeWidget() {return NULL;}; virtual void setSecurityLabelsError() {}; virtual SecurityLabel getSelectedSecurityLabel() {return SecurityLabel();}; virtual void setInputEnabled(bool /*enabled*/) {}; boost::signal<void ()> onClosed; boost::signal<void ()> onAllMessagesRead; boost::signal<void (const String&)> onSendMessageRequest; String name_; + String lastMessageBody_; std::vector<SecurityLabel> labels_; bool labelsEnabled_; }; } |