diff options
author | Tobias Markmann <tm@ayena.de> | 2016-03-08 13:36:27 (GMT) |
---|---|---|
committer | Tobias Markmann <tm@ayena.de> | 2016-03-29 14:13:20 (GMT) |
commit | 74e51310d27e9d9a66d2d790360549c48abec8d1 (patch) | |
tree | d350bf7cd62c144e561841b4a228b0f60cb76957 /Swift/Controllers/Chat | |
parent | c26314684cd4e6140e5ea882285c2076505bd53d (diff) | |
download | swift-74e51310d27e9d9a66d2d790360549c48abec8d1.zip swift-74e51310d27e9d9a66d2d790360549c48abec8d1.tar.bz2 |
Refactored keyword highlighting
This commit changes the ChatWindow/ChatView APIs to not pass
highlights actions as additional parameters but instead they
are now part of the ChatWindow::ChatMessage and its parts.
This allows the controllers to do highlighting in one single
place and play sound actions on the highlighted message in
a single place.
On a highlighted message only unique sounds are played and
they are played in sequence of the rules that matched
the message.
Test-Information:
Adjusted the existing unit tests accordingly. Added unit
tests that check reduplication of highlight action sounds
and that the sound actions are emitted correctly.
Manually verified that highlight sound actions with and
without duplicated sounds are audible on OS X 10.11.3.
Change-Id: I68c88e0d285d79d87b2997ed29d92b140480b394
Diffstat (limited to 'Swift/Controllers/Chat')
-rw-r--r-- | Swift/Controllers/Chat/ChatController.cpp | 8 | ||||
-rw-r--r-- | Swift/Controllers/Chat/ChatController.h | 4 | ||||
-rw-r--r-- | Swift/Controllers/Chat/ChatControllerBase.cpp | 72 | ||||
-rw-r--r-- | Swift/Controllers/Chat/ChatControllerBase.h | 13 | ||||
-rw-r--r-- | Swift/Controllers/Chat/ChatMessageParser.cpp | 10 | ||||
-rw-r--r-- | Swift/Controllers/Chat/MUCController.cpp | 12 | ||||
-rw-r--r-- | Swift/Controllers/Chat/MUCController.h | 6 | ||||
-rw-r--r-- | Swift/Controllers/Chat/UnitTest/ChatMessageParserTest.cpp | 47 | ||||
-rw-r--r-- | Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp | 90 |
9 files changed, 195 insertions, 67 deletions
diff --git a/Swift/Controllers/Chat/ChatController.cpp b/Swift/Controllers/Chat/ChatController.cpp index a80eee5..7f62c36 100644 --- a/Swift/Controllers/Chat/ChatController.cpp +++ b/Swift/Controllers/Chat/ChatController.cpp @@ -208,10 +208,10 @@ void ChatController::preHandleIncomingMessage(boost::shared_ptr<MessageEvent> me } } -void ChatController::postHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent, const HighlightAction& highlight) { +void ChatController::postHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent, const ChatWindow::ChatMessage& chatMessage) { eventController_->handleIncomingEvent(messageEvent); if (!messageEvent->getConcluded()) { - highlighter_->handleHighlightAction(highlight); + handleHighlightActions(chatMessage); } } @@ -316,9 +316,9 @@ void ChatController::postSendMessage(const std::string& body, boost::shared_ptr< boost::shared_ptr<Replace> replace = sentStanza->getPayload<Replace>(); if (replace) { eraseIf(unackedStanzas_, PairSecondEquals<boost::shared_ptr<Stanza>, std::string>(myLastMessageUIID_)); - replaceMessage(body, myLastMessageUIID_, true, boost::posix_time::microsec_clock::universal_time(), HighlightAction()); + replaceMessage(chatMessageParser_->parseMessageBody(body, "", true), myLastMessageUIID_, boost::posix_time::microsec_clock::universal_time()); } else { - myLastMessageUIID_ = addMessage(body, QT_TRANSLATE_NOOP("", "me"), true, labelsEnabled_ ? chatWindow_->getSelectedSecurityLabel().getLabel() : boost::shared_ptr<SecurityLabel>(), avatarManager_->getAvatarPath(selfJID_), boost::posix_time::microsec_clock::universal_time(), HighlightAction()); + myLastMessageUIID_ = addMessage(chatMessageParser_->parseMessageBody(body, "", true), QT_TRANSLATE_NOOP("", "me"), true, labelsEnabled_ ? chatWindow_->getSelectedSecurityLabel().getLabel() : boost::shared_ptr<SecurityLabel>(), avatarManager_->getAvatarPath(selfJID_), boost::posix_time::microsec_clock::universal_time()); } if (stanzaChannel_->getStreamManagementEnabled() && !myLastMessageUIID_.empty() ) { diff --git a/Swift/Controllers/Chat/ChatController.h b/Swift/Controllers/Chat/ChatController.h index 9ee82eb..6ec19df 100644 --- a/Swift/Controllers/Chat/ChatController.h +++ b/Swift/Controllers/Chat/ChatController.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2015 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -52,7 +52,7 @@ namespace Swift { virtual bool isIncomingMessageFromMe(boost::shared_ptr<Message> message) SWIFTEN_OVERRIDE; virtual void postSendMessage(const std::string &body, boost::shared_ptr<Stanza> sentStanza) SWIFTEN_OVERRIDE; virtual void preHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent) SWIFTEN_OVERRIDE; - virtual void postHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent, const HighlightAction&) SWIFTEN_OVERRIDE; + virtual void postHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent, const ChatWindow::ChatMessage& chatMessage) SWIFTEN_OVERRIDE; virtual void preSendMessageRequest(boost::shared_ptr<Message>) SWIFTEN_OVERRIDE; virtual std::string senderHighlightNameFromMessage(const JID& from) SWIFTEN_OVERRIDE; virtual std::string senderDisplayNameFromMessage(const JID& from) SWIFTEN_OVERRIDE; diff --git a/Swift/Controllers/Chat/ChatControllerBase.cpp b/Swift/Controllers/Chat/ChatControllerBase.cpp index fef3e7a..c790cce 100644 --- a/Swift/Controllers/Chat/ChatControllerBase.cpp +++ b/Swift/Controllers/Chat/ChatControllerBase.cpp @@ -208,19 +208,50 @@ bool ChatControllerBase::hasOpenWindow() const { return chatWindow_ && chatWindow_->isVisible(); } -std::string ChatControllerBase::addMessage(const std::string& message, const std::string& senderName, bool senderIsSelf, const boost::shared_ptr<SecurityLabel> label, const boost::filesystem::path& avatarPath, const boost::posix_time::ptime& time, const HighlightAction& highlight) { +ChatWindow::ChatMessage ChatControllerBase::buildChatWindowChatMessage(const std::string& message, bool senderIsSelf, const HighlightAction& fullMessageHighlightAction) { + ChatWindow::ChatMessage chatMessage; if (boost::starts_with(message, "/me ")) { - return chatWindow_->addAction(chatMessageParser_->parseMessageBody(String::getSplittedAtFirst(message, ' ').second), senderName, senderIsSelf, label, pathToString(avatarPath), time, highlight); - } else { - return chatWindow_->addMessage(chatMessageParser_->parseMessageBody(message,highlighter_->getNick(),senderIsSelf), senderName, senderIsSelf, label, pathToString(avatarPath), time, highlight); + chatMessage = chatMessageParser_->parseMessageBody(String::getSplittedAtFirst(message, ' ').second); + } + else { + chatMessage = chatMessageParser_->parseMessageBody(message, highlighter_->getNick(), senderIsSelf); } + chatMessage.setFullMessageHighlightAction(fullMessageHighlightAction); + return chatMessage; } -void ChatControllerBase::replaceMessage(const std::string& message, const std::string& id, bool senderIsSelf, const boost::posix_time::ptime& time, const HighlightAction& highlight) { - if (boost::starts_with(message, "/me ")) { - chatWindow_->replaceWithAction(chatMessageParser_->parseMessageBody(String::getSplittedAtFirst(message, ' ').second), id, time, highlight); - } else { - chatWindow_->replaceMessage(chatMessageParser_->parseMessageBody(message,highlighter_->getNick(),senderIsSelf), id, time, highlight); +void ChatControllerBase::handleHighlightActions(const ChatWindow::ChatMessage& chatMessage) { + std::set<std::string> playedSounds; + if (chatMessage.getFullMessageHighlightAction().playSound()) { + highlighter_->handleHighlightAction(chatMessage.getFullMessageHighlightAction()); + playedSounds.insert(chatMessage.getFullMessageHighlightAction().getSoundFile()); + } + foreach(boost::shared_ptr<ChatWindow::ChatMessagePart> part, chatMessage.getParts()) { + boost::shared_ptr<ChatWindow::ChatHighlightingMessagePart> highlightMessage = boost::dynamic_pointer_cast<ChatWindow::ChatHighlightingMessagePart>(part); + if (highlightMessage && highlightMessage->action.playSound()) { + if (playedSounds.find(highlightMessage->action.getSoundFile()) == playedSounds.end()) { + highlighter_->handleHighlightAction(highlightMessage->action); + playedSounds.insert(highlightMessage->action.getSoundFile()); + } + } + } +} + +std::string ChatControllerBase::addMessage(const ChatWindow::ChatMessage& chatMessage, const std::string& senderName, bool senderIsSelf, const boost::shared_ptr<SecurityLabel> label, const boost::filesystem::path& avatarPath, const boost::posix_time::ptime& time) { + if (chatMessage.isMeCommand()) { + return chatWindow_->addAction(chatMessage, senderName, senderIsSelf, label, pathToString(avatarPath), time); + } + else { + return chatWindow_->addMessage(chatMessage, senderName, senderIsSelf, label, pathToString(avatarPath), time); + } +} + +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); } } @@ -236,9 +267,11 @@ void ChatControllerBase::handleIncomingMessage(boost::shared_ptr<MessageEvent> m targetedUnreadMessages_.push_back(messageEvent); } } + boost::shared_ptr<Message> message = messageEvent->getStanza(); - std::string body = message->getBody().get_value_or(""); - HighlightAction highlight; + 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>())); @@ -280,22 +313,25 @@ void ChatControllerBase::handleIncomingMessage(boost::shared_ptr<MessageEvent> m onActivity(body); // Highlight + HighlightAction fullMessageHighlight; if (!isIncomingMessageFromMe(message)) { - highlight = highlighter_->findAction(body, senderHighlightNameFromMessage(from)); + fullMessageHighlight = highlighter_->findFirstFullMessageMatchAction(body, senderHighlightNameFromMessage(from)); } boost::shared_ptr<Replace> replace = message->getPayload<Replace>(); + bool senderIsSelf = isIncomingMessageFromMe(message); if (replace) { - std::string body = message->getBody().get_value_or(""); // Should check if the user has a previous message std::map<JID, std::string>::iterator lastMessage; lastMessage = lastMessagesUIID_.find(from); if (lastMessage != lastMessagesUIID_.end()) { - replaceMessage(body, lastMessagesUIID_[from], isIncomingMessageFromMe(message), timeStamp, highlight); + chatMessage = buildChatWindowChatMessage(body, senderIsSelf, fullMessageHighlight); + replaceMessage(chatMessage, lastMessagesUIID_[from], timeStamp); } } else { - addMessageHandleIncomingMessage(from, body, isIncomingMessageFromMe(message), label, timeStamp, highlight); + chatMessage = buildChatWindowChatMessage(body, senderIsSelf, fullMessageHighlight); + addMessageHandleIncomingMessage(from, chatMessage, senderIsSelf, label, timeStamp); } logMessage(body, from, selfJID_, timeStamp, true); @@ -303,11 +339,11 @@ void ChatControllerBase::handleIncomingMessage(boost::shared_ptr<MessageEvent> m chatWindow_->show(); chatWindow_->setUnreadMessageCount(boost::numeric_cast<int>(unreadMessages_.size())); onUnreadCountChanged(); - postHandleIncomingMessage(messageEvent, highlight); + postHandleIncomingMessage(messageEvent, chatMessage); } -void ChatControllerBase::addMessageHandleIncomingMessage(const JID& from, const std::string& message, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const boost::posix_time::ptime& timeStamp, const HighlightAction& highlight) { - lastMessagesUIID_[from] = addMessage(message, senderDisplayNameFromMessage(from), senderIsSelf, label, avatarManager_->getAvatarPath(from), timeStamp, highlight); +void ChatControllerBase::addMessageHandleIncomingMessage(const JID& from, const ChatWindow::ChatMessage& message, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const boost::posix_time::ptime& timeStamp) { + lastMessagesUIID_[from] = addMessage(message, senderDisplayNameFromMessage(from), senderIsSelf, label, avatarManager_->getAvatarPath(from), timeStamp); } std::string ChatControllerBase::getErrorMessage(boost::shared_ptr<ErrorPayload> error) { diff --git a/Swift/Controllers/Chat/ChatControllerBase.h b/Swift/Controllers/Chat/ChatControllerBase.h index 4e68a2b..98f0ab0 100644 --- a/Swift/Controllers/Chat/ChatControllerBase.h +++ b/Swift/Controllers/Chat/ChatControllerBase.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2015 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -55,8 +55,8 @@ namespace Swift { bool hasOpenWindow() const; virtual void setAvailableServerFeatures(boost::shared_ptr<DiscoInfo> info); void handleIncomingMessage(boost::shared_ptr<MessageEvent> message); - std::string addMessage(const std::string& message, const std::string& senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const boost::filesystem::path& avatarPath, const boost::posix_time::ptime& time, const HighlightAction& highlight); - void replaceMessage(const std::string& message, const std::string& id, bool senderIsSelf, const boost::posix_time::ptime& time, const HighlightAction& highlight); + std::string addMessage(const ChatWindow::ChatMessage& chatMessage, const std::string& senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const boost::filesystem::path& avatarPath, const boost::posix_time::ptime& time); + void replaceMessage(const ChatWindow::ChatMessage& chatMessage, const std::string& id, const boost::posix_time::ptime& time); virtual void setOnline(bool online); void setEnabled(bool enabled); virtual void setToJID(const JID& jid) {toJID_ = jid;} @@ -82,8 +82,8 @@ namespace Swift { virtual std::string senderHighlightNameFromMessage(const JID& from) = 0; virtual bool isIncomingMessageFromMe(boost::shared_ptr<Message>) = 0; virtual void preHandleIncomingMessage(boost::shared_ptr<MessageEvent>) {} - virtual void addMessageHandleIncomingMessage(const JID& from, const std::string& message, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const boost::posix_time::ptime& time, const HighlightAction& highlight); - virtual void postHandleIncomingMessage(boost::shared_ptr<MessageEvent>, const HighlightAction&) {} + virtual void addMessageHandleIncomingMessage(const JID& from, const ChatWindow::ChatMessage& message, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const boost::posix_time::ptime& time); + virtual void postHandleIncomingMessage(boost::shared_ptr<MessageEvent>, const ChatWindow::ChatMessage&) {} virtual void preSendMessageRequest(boost::shared_ptr<Message>) {} virtual bool isFromContact(const JID& from); virtual boost::optional<boost::posix_time::ptime> getMessageTimestamp(boost::shared_ptr<Message>) const = 0; @@ -95,11 +95,14 @@ namespace Swift { /** JID any iq for account should go to - bare except for PMs */ virtual JID getBaseJID(); virtual void logMessage(const std::string& message, const JID& fromJID, const JID& toJID, const boost::posix_time::ptime& timeStamp, bool isIncoming) = 0; + ChatWindow::ChatMessage buildChatWindowChatMessage(const std::string& message, bool senderIsSelf, const HighlightAction& fullMessageHighlightAction); + void handleHighlightActions(const ChatWindow::ChatMessage& chatMessage); private: IDGenerator idGenerator_; std::string lastSentMessageStanzaID_; void createDayChangeTimer(); + void handleSendMessageRequest(const std::string &body, bool isCorrectionMessage); void handleAllMessagesRead(); void handleSecurityLabelsCatalogResponse(boost::shared_ptr<SecurityLabelsCatalog>, ErrorPayload::ref error); diff --git a/Swift/Controllers/Chat/ChatMessageParser.cpp b/Swift/Controllers/Chat/ChatMessageParser.cpp index 666ec2f..53c7588 100644 --- a/Swift/Controllers/Chat/ChatMessageParser.cpp +++ b/Swift/Controllers/Chat/ChatMessageParser.cpp @@ -1,23 +1,22 @@ /* - * Copyright (c) 2013-2015 Isode Limited. + * Copyright (c) 2013-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include <Swift/Controllers/Chat/ChatMessageParser.h> -#include <vector> #include <utility> +#include <vector> -#include <boost/smart_ptr/make_shared.hpp> #include <boost/algorithm/string.hpp> +#include <boost/smart_ptr/make_shared.hpp> #include <Swiften/Base/Regex.h> #include <Swiften/Base/foreach.h> #include <SwifTools/Linkify.h> - namespace Swift { ChatMessageParser::ChatMessageParser(const std::map<std::string, std::string>& emoticons, HighlightRulesListPtr highlightRules, bool mucMode) @@ -170,8 +169,7 @@ namespace Swift { } boost::shared_ptr<ChatWindow::ChatHighlightingMessagePart> highlightPart = boost::make_shared<ChatWindow::ChatHighlightingMessagePart>(); highlightPart->text = match.str(); - highlightPart->foregroundColor = rule.getAction().getTextColor(); - highlightPart->backgroundColor = rule.getAction().getTextBackground(); + highlightPart->action = rule.getAction(); newMessage.append(highlightPart); start = matchEnd; } diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp index 409fe1f..0bb670d 100644 --- a/Swift/Controllers/Chat/MUCController.cpp +++ b/Swift/Controllers/Chat/MUCController.cpp @@ -576,23 +576,23 @@ void MUCController::preHandleIncomingMessage(boost::shared_ptr<MessageEvent> mes } } -void MUCController::addMessageHandleIncomingMessage(const JID& from, const std::string& message, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const boost::posix_time::ptime& time, const HighlightAction& highlight) { +void MUCController::addMessageHandleIncomingMessage(const JID& from, const ChatWindow::ChatMessage& message, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const boost::posix_time::ptime& time) { if (from.isBare()) { - chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(str(format(QT_TRANSLATE_NOOP("", "%1%")) % message)), ChatWindow::DefaultDirection); + chatWindow_->addSystemMessage(message, ChatWindow::DefaultDirection); } else { - ChatControllerBase::addMessageHandleIncomingMessage(from, message, senderIsSelf, label, time, highlight); + ChatControllerBase::addMessageHandleIncomingMessage(from, message, senderIsSelf, label, time); } } -void MUCController::postHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent, const HighlightAction& highlight) { +void MUCController::postHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent, const ChatWindow::ChatMessage& chatMessage) { boost::shared_ptr<Message> message = messageEvent->getStanza(); if (joined_ && messageEvent->getStanza()->getFrom().getResource() != nick_ && !message->getPayload<Delay>()) { if (messageTargetsMe(message) || isImpromptu_) { eventController_->handleIncomingEvent(messageEvent); } if (!messageEvent->getConcluded()) { - highlighter_->handleHighlightAction(highlight); + handleHighlightActions(chatMessage); } } } @@ -1074,7 +1074,7 @@ void MUCController::addRecentLogs() { bool senderIsSelf = nick_ == message.getFromJID().getResource(); // the chatWindow uses utc timestamps - addMessage(message.getMessage(), senderDisplayNameFromMessage(message.getFromJID()), senderIsSelf, boost::shared_ptr<SecurityLabel>(new SecurityLabel()), avatarManager_->getAvatarPath(message.getFromJID()), message.getTime() - boost::posix_time::hours(message.getOffset()), HighlightAction()); + addMessage(chatMessageParser_->parseMessageBody(message.getMessage()), senderDisplayNameFromMessage(message.getFromJID()), senderIsSelf, boost::shared_ptr<SecurityLabel>(new SecurityLabel()), avatarManager_->getAvatarPath(message.getFromJID()), message.getTime() - boost::posix_time::hours(message.getOffset())); } } diff --git a/Swift/Controllers/Chat/MUCController.h b/Swift/Controllers/Chat/MUCController.h index a08d541..0fb739a 100644 --- a/Swift/Controllers/Chat/MUCController.h +++ b/Swift/Controllers/Chat/MUCController.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2015 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -81,8 +81,8 @@ namespace Swift { virtual std::string senderDisplayNameFromMessage(const JID& from) SWIFTEN_OVERRIDE; virtual boost::optional<boost::posix_time::ptime> getMessageTimestamp(boost::shared_ptr<Message> message) const SWIFTEN_OVERRIDE; virtual void preHandleIncomingMessage(boost::shared_ptr<MessageEvent>) SWIFTEN_OVERRIDE; - virtual void addMessageHandleIncomingMessage(const JID& from, const std::string& message, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const boost::posix_time::ptime& time, const HighlightAction& highlight) SWIFTEN_OVERRIDE; - virtual void postHandleIncomingMessage(boost::shared_ptr<MessageEvent>, const HighlightAction&) SWIFTEN_OVERRIDE; + virtual void addMessageHandleIncomingMessage(const JID& from, const ChatWindow::ChatMessage& message, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const boost::posix_time::ptime& time) SWIFTEN_OVERRIDE; + virtual void postHandleIncomingMessage(boost::shared_ptr<MessageEvent>, const ChatWindow::ChatMessage& chatMessage) SWIFTEN_OVERRIDE; virtual void cancelReplaces() SWIFTEN_OVERRIDE; virtual void logMessage(const std::string& message, const JID& fromJID, const JID& toJID, const boost::posix_time::ptime& timeStamp, bool isIncoming) SWIFTEN_OVERRIDE; diff --git a/Swift/Controllers/Chat/UnitTest/ChatMessageParserTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatMessageParserTest.cpp index 1b92bb6..6748b9e 100644 --- a/Swift/Controllers/Chat/UnitTest/ChatMessageParserTest.cpp +++ b/Swift/Controllers/Chat/UnitTest/ChatMessageParserTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015 Isode Limited. + * Copyright (c) 2013-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -49,9 +49,11 @@ public: CPPUNIT_ASSERT_EQUAL(path, part->imagePath); } - void assertHighlight(const ChatWindow::ChatMessage& result, size_t index, const std::string& text) { - boost::shared_ptr<ChatWindow::ChatHighlightingMessagePart> part = boost::dynamic_pointer_cast<ChatWindow::ChatHighlightingMessagePart>(result.getParts()[index]); - CPPUNIT_ASSERT_EQUAL(text, part->text); +#define assertHighlight(RESULT, INDEX, TEXT, EXPECTED_HIGHLIGHT) \ + { \ + boost::shared_ptr<ChatWindow::ChatHighlightingMessagePart> part = boost::dynamic_pointer_cast<ChatWindow::ChatHighlightingMessagePart>(RESULT.getParts()[INDEX]); \ + CPPUNIT_ASSERT_EQUAL(std::string(TEXT), part->text); \ + CPPUNIT_ASSERT(EXPECTED_HIGHLIGHT == part->action); \ } void assertURL(const ChatWindow::ChatMessage& result, size_t index, const std::string& text) { @@ -108,13 +110,14 @@ public: ChatWindow::ChatMessage result = testling.parseMessageBody(no_special_message); assertText(result, 0, no_special_message); - testling = ChatMessageParser(emoticons_, ruleListFromKeyword("trigger", false, false)); + HighlightRulesListPtr highlightRuleList = ruleListFromKeyword("trigger", false, false); + testling = ChatMessageParser(emoticons_, highlightRuleList); result = testling.parseMessageBody(":) shiny :( trigger :) http://wonderland.lit/blah http://denmark.lit boom boom"); assertEmoticon(result, 0, smile1_, smile1Path_); assertText(result, 1, " shiny "); assertEmoticon(result, 2, smile2_, smile2Path_); assertText(result, 3, " "); - assertHighlight(result, 4, "trigger"); + assertHighlight(result, 4, "trigger", highlightRuleList->getRule(0).getAction()); assertText(result, 5, " "); assertEmoticon(result, 6, smile1_, smile1Path_); assertText(result, 7, " "); @@ -126,7 +129,7 @@ public: testling = ChatMessageParser(emoticons_, ruleListFromKeyword("trigger", false, false)); result = testling.parseMessageBody("testtriggermessage"); assertText(result, 0, "test"); - assertHighlight(result, 1, "trigger"); + assertHighlight(result, 1, "trigger", highlightRuleList->getRule(0).getAction()); assertText(result, 2, "message"); testling = ChatMessageParser(emoticons_, ruleListFromKeyword("trigger", false, true)); @@ -139,32 +142,32 @@ public: testling = ChatMessageParser(emoticons_, ruleListFromKeyword("trigger", false, false)); result = testling.parseMessageBody("TrIgGeR"); - assertHighlight(result, 0, "TrIgGeR"); + assertHighlight(result, 0, "TrIgGeR", highlightRuleList->getRule(0).getAction()); testling = ChatMessageParser(emoticons_, ruleListFromKeyword("trigger", false, false)); result = testling.parseMessageBody("partialTrIgGeRmatch"); assertText(result, 0, "partial"); - assertHighlight(result, 1, "TrIgGeR"); + assertHighlight(result, 1, "TrIgGeR", highlightRuleList->getRule(0).getAction()); assertText(result, 2, "match"); testling = ChatMessageParser(emoticons_, ruleListFromKeywords(ruleFromKeyword("one", false, false), ruleFromKeyword("three", false, false))); result = testling.parseMessageBody("zero one two three"); assertText(result, 0, "zero "); - assertHighlight(result, 1, "one"); + assertHighlight(result, 1, "one", highlightRuleList->getRule(0).getAction()); assertText(result, 2, " two "); - assertHighlight(result, 3, "three"); + assertHighlight(result, 3, "three", highlightRuleList->getRule(0).getAction()); testling = ChatMessageParser(emoticons_, ruleListFromKeywords(ruleFromKeyword("one", false, false), ruleFromKeyword("three", false, false))); result = testling.parseMessageBody("zero oNe two tHrEe"); assertText(result, 0, "zero "); - assertHighlight(result, 1, "oNe"); + assertHighlight(result, 1, "oNe", highlightRuleList->getRule(0).getAction()); assertText(result, 2, " two "); - assertHighlight(result, 3, "tHrEe"); + assertHighlight(result, 3, "tHrEe", highlightRuleList->getRule(0).getAction()); testling = ChatMessageParser(emoticons_, ruleListFromKeywords(ruleFromKeyword("one", false, false), ruleFromKeyword("three", true, false))); result = testling.parseMessageBody("zero oNe two tHrEe"); assertText(result, 0, "zero "); - assertHighlight(result, 1, "oNe"); + assertHighlight(result, 1, "oNe", highlightRuleList->getRule(0).getAction()); assertText(result, 2, " two tHrEe"); testling = ChatMessageParser(emoticons_, ruleListFromKeywords(ruleFromKeyword("one", true, false), ruleFromKeyword("three", true, false))); @@ -174,19 +177,19 @@ public: testling = ChatMessageParser(emoticons_, ruleListFromKeywords(ruleFromKeyword("one", false, false), ruleFromKeyword("three", false, false))); result = testling.parseMessageBody("zeroonetwothree"); assertText(result, 0, "zero"); - assertHighlight(result, 1, "one"); + assertHighlight(result, 1, "one", highlightRuleList->getRule(0).getAction()); assertText(result, 2, "two"); - assertHighlight(result, 3, "three"); + assertHighlight(result, 3, "three", highlightRuleList->getRule(0).getAction()); testling = ChatMessageParser(emoticons_, ruleListFromKeywords(ruleFromKeyword("one", true, false), ruleFromKeyword("three", false, false))); result = testling.parseMessageBody("zeroOnEtwoThReE"); assertText(result, 0, "zeroOnEtwo"); - assertHighlight(result, 1, "ThReE"); + assertHighlight(result, 1, "ThReE", highlightRuleList->getRule(0).getAction()); testling = ChatMessageParser(emoticons_, ruleListFromKeywords(ruleFromKeyword("one", false, true), ruleFromKeyword("three", false, false))); result = testling.parseMessageBody("zeroonetwothree"); assertText(result, 0, "zeroonetwo"); - assertHighlight(result, 1, "three"); + assertHighlight(result, 1, "three", highlightRuleList->getRule(0).getAction()); testling = ChatMessageParser(emoticons_, ruleListFromKeywords(ruleFromKeyword("one", false, true), ruleFromKeyword("three", false, true))); result = testling.parseMessageBody("zeroonetwothree"); @@ -194,7 +197,7 @@ public: testling = ChatMessageParser(emoticons_, ruleListWithNickHighlight()); result = testling.parseMessageBody("Alice", "Alice"); - assertHighlight(result, 0, "Alice"); + assertHighlight(result, 0, "Alice", highlightRuleList->getRule(0).getAction()); testling = ChatMessageParser(emoticons_, ruleListWithNickHighlight()); result = testling.parseMessageBody("TextAliceText", "Alice"); @@ -203,18 +206,18 @@ public: testling = ChatMessageParser(emoticons_, ruleListWithNickHighlight()); result = testling.parseMessageBody("Text Alice Text", "Alice"); assertText(result, 0, "Text "); - assertHighlight(result, 1, "Alice"); + assertHighlight(result, 1, "Alice", highlightRuleList->getRule(0).getAction()); assertText(result, 2, " Text"); testling = ChatMessageParser(emoticons_, ruleListWithNickHighlight()); result = testling.parseMessageBody("Alice Text", "Alice"); - assertHighlight(result, 0, "Alice"); + assertHighlight(result, 0, "Alice", highlightRuleList->getRule(0).getAction()); assertText(result, 1, " Text"); testling = ChatMessageParser(emoticons_, ruleListWithNickHighlight()); result = testling.parseMessageBody("Text Alice", "Alice"); assertText(result, 0, "Text "); - assertHighlight(result, 1, "Alice"); + assertHighlight(result, 1, "Alice", highlightRuleList->getRule(0).getAction()); } void testOneEmoticon() { diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp index 4f8cf5a..31d54db 100644 --- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp +++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp @@ -4,6 +4,9 @@ * See the COPYING file for more information. */ +#include <set> +#include <map> + #include <boost/bind.hpp> #include <cppunit/extensions/HelperMacros.h> @@ -75,6 +78,8 @@ class ChatsManagerTest : public CppUnit::TestFixture { CPPUNIT_TEST(testChatControllerFullJIDBindingOnTypingAndNotActive); CPPUNIT_TEST(testChatControllerPMPresenceHandling); CPPUNIT_TEST(testLocalMUCServiceDiscoveryResetOnDisconnect); + CPPUNIT_TEST(testChatControllerHighlightingNotificationTesting); + CPPUNIT_TEST(testChatControllerHighlightingNotificationDeduplicateSounds); CPPUNIT_TEST_SUITE_END(); public: @@ -110,6 +115,9 @@ public: wbSessionManager_ = new WhiteboardSessionManager(iqRouter_, stanzaChannel_, presenceOracle_, entityCapsProvider_); wbManager_ = new WhiteboardManager(whiteboardWindowFactory_, uiEventStream_, nickResolver_, wbSessionManager_); highlightManager_ = new HighlightManager(settings_); + handledHighlightActions_ = 0; + soundsPlayed_.clear(); + highlightManager_->onHighlight.connect(boost::bind(&ChatsManagerTest::handleHighlightAction, this, _1)); crypto_ = PlatformCryptoProvider::create(); vcardStorage_ = new VCardMemoryStorage(crypto_); @@ -156,7 +164,7 @@ public: void testFirstOpenWindowIncoming() { 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); boost::shared_ptr<Message> message(new Message()); @@ -724,6 +732,77 @@ public: CPPUNIT_ASSERT(stanzaContactOnRoster->getPayload<DeliveryReceipt>() != 0); } + void testChatControllerHighlightingNotificationTesting() { + HighlightRule keywordRuleA; + keywordRuleA.setMatchChat(true); + std::vector<std::string> keywordsA; + keywordsA.push_back("Romeo"); + keywordRuleA.setKeywords(keywordsA); + keywordRuleA.getAction().setTextColor("yellow"); + keywordRuleA.getAction().setPlaySound(true); + highlightManager_->insertRule(0, keywordRuleA); + + HighlightRule keywordRuleB; + keywordRuleB.setMatchChat(true); + std::vector<std::string> keywordsB; + keywordsB.push_back("Juliet"); + keywordRuleB.setKeywords(keywordsB); + keywordRuleB.getAction().setTextColor("green"); + keywordRuleB.getAction().setPlaySound(true); + keywordRuleB.getAction().setSoundFile("/tmp/someotherfile.wav"); + highlightManager_->insertRule(0, keywordRuleB); + + JID messageJID = JID("testling@test.com"); + + MockChatWindow* window = new MockChatWindow(); + mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window); + + boost::shared_ptr<Message> 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.getAction().getSoundFile()) != soundsPlayed_.end()); + CPPUNIT_ASSERT(soundsPlayed_.find(keywordRuleB.getAction().getSoundFile()) != soundsPlayed_.end()); + } + + void testChatControllerHighlightingNotificationDeduplicateSounds() { + HighlightRule keywordRuleA; + keywordRuleA.setMatchChat(true); + std::vector<std::string> keywordsA; + keywordsA.push_back("Romeo"); + keywordRuleA.setKeywords(keywordsA); + keywordRuleA.getAction().setTextColor("yellow"); + keywordRuleA.getAction().setPlaySound(true); + highlightManager_->insertRule(0, keywordRuleA); + + HighlightRule keywordRuleB; + keywordRuleB.setMatchChat(true); + std::vector<std::string> keywordsB; + keywordsB.push_back("Juliet"); + keywordRuleB.setKeywords(keywordsB); + keywordRuleB.getAction().setTextColor("green"); + keywordRuleB.getAction().setPlaySound(true); + highlightManager_->insertRule(0, keywordRuleB); + + JID messageJID = JID("testling@test.com"); + + MockChatWindow* window = new MockChatWindow(); + mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window); + + boost::shared_ptr<Message> 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.getAction().getSoundFile()) != soundsPlayed_.end()); + CPPUNIT_ASSERT(soundsPlayed_.find(keywordRuleB.getAction().getSoundFile()) != soundsPlayed_.end()); + } + private: boost::shared_ptr<Message> makeDeliveryReceiptTestMessage(const JID& from, const std::string& id) { boost::shared_ptr<Message> message = boost::make_shared<Message>(); @@ -738,6 +817,13 @@ private: return static_cast<size_t>(i); } + void handleHighlightAction(const HighlightAction& action) { + handledHighlightActions_++; + if (action.playSound()) { + soundsPlayed_.insert(action.getSoundFile()); + } + } + private: JID jid_; ChatsManager* manager_; @@ -775,6 +861,8 @@ private: CryptoProvider* crypto_; VCardStorage* vcardStorage_; std::map<std::string, std::string> emoticons_; + int handledHighlightActions_; + std::set<std::string> soundsPlayed_; }; CPPUNIT_TEST_SUITE_REGISTRATION(ChatsManagerTest); |