summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTobias Markmann <tm@ayena.de>2017-02-19 19:55:46 (GMT)
committerTobias Markmann <tm@ayena.de>2017-03-03 11:26:38 (GMT)
commit5fa6933f9cc3e6d0df8c6896871cbe6cc09a9492 (patch)
tree96854d1b7c2b85f959aa2e1bab215e9fcdabd94d
parent901ca8337c983f098394c7d4889f74aad452b1d0 (diff)
downloadswift-5fa6933f9cc3e6d0df8c6896871cbe6cc09a9492.zip
swift-5fa6933f9cc3e6d0df8c6896871cbe6cc09a9492.tar.bz2
Do not clear join/leave queue for MUC rooms on CSN messages
Previously chat-state notification messages would cause the join/leave queue of a MUC room to be cleared, resulting in taking up more vertical space than it had to. Test-Information: Compared two Swift builds (one with this patch and one without) in a room where some occupants would send CSN messages from time to time. With this patch, a CSN message clearly does not cause the join/leave queue to be cleared. Added unit test to verify new behaviour. Tested on macOS 10.12.3 with Qt 5.7.1. Change-Id: I0aee733fa5d16bbfb497a17b3d7a3ffe3fea8f26
-rw-r--r--Swift/Controllers/Chat/MUCController.cpp8
-rw-r--r--Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp68
2 files changed, 75 insertions, 1 deletions
diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp
index 8fa98f6..545eab5 100644
--- a/Swift/Controllers/Chat/MUCController.cpp
+++ b/Swift/Controllers/Chat/MUCController.cpp
@@ -512,62 +512,68 @@ std::string MUCController::roleToFriendlyName(MUCOccupant::Role role) {
}
assert(false);
return "";
}
std::string MUCController::roleToSortName(MUCOccupant::Role role) {
switch (role) {
case MUCOccupant::Moderator: return "1";
case MUCOccupant::Participant: return "2";
case MUCOccupant::Visitor: return "3";
case MUCOccupant::NoRole: return "4";
}
assert(false);
return "5";
}
JID MUCController::nickToJID(const std::string& nick) {
return muc_->getJID().withResource(nick);
}
bool MUCController::messageTargetsMe(std::shared_ptr<Message> message) {
std::string stringRegexp(".*\\b" + boost::to_lower_copy(nick_) + "\\b.*");
boost::regex myRegexp(stringRegexp);
return boost::regex_match(boost::to_lower_copy(message->getBody().get_value_or("")), myRegexp);
}
void MUCController::preHandleIncomingMessage(std::shared_ptr<MessageEvent> messageEvent) {
if (messageEvent->getStanza()->getType() == Message::Groupchat) {
lastActivity_ = boost::posix_time::microsec_clock::universal_time();
}
- clearPresenceQueue();
std::shared_ptr<Message> message = messageEvent->getStanza();
+
+ // This avoids clearing join/leave queue for chat state notification messages
+ // which are not readable (e.g. have no body content).
+ if (!(!messageEvent->isReadable() && message->getPayload<ChatState>())) {
+ clearPresenceQueue();
+ }
+
if (joined_ && messageEvent->getStanza()->getFrom().getResource() != nick_ && messageTargetsMe(message) && !message->getPayload<Delay>() && messageEvent->isReadable()) {
chatWindow_->flash();
}
else {
messageEvent->setTargetsMe(false);
}
if (messageEvent->isReadable() && isImpromptu_) {
chatWindow_->flash(); /* behave like a regular char*/
}
if (joined_) {
std::string nick = message->getFrom().getResource();
if (nick != nick_ && currentOccupants_.find(nick) != currentOccupants_.end()) {
completer_->addWord(nick);
}
}
/*Buggy implementations never send the status code, so use an incoming message as a hint that joining's done (e.g. the old ejabberd on psi-im.org).*/
receivedActivity();
joined_ = true;
if (message->hasSubject() && !message->getPayload<Body>() && !message->getPayload<Thread>()) {
if (!isInitialJoin_) {
displaySubjectIfChanged(message->getSubject());
}
isInitialJoin_ = false;
chatWindow_->setSubject(message->getSubject());
doneGettingHistory_ = true;
subject_ = message->getSubject();
}
if (!doneGettingHistory_ && !message->getPayload<Delay>()) {
diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
index 00df3da..1958408 100644
--- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
+++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
@@ -89,60 +89,61 @@ class DummyNotifier : public Notifier {
Type type;
std::string subject;
std::string description;
boost::filesystem::path picture;
boost::function<void()> callback;
};
public:
std::vector<Notification> notifications;
};
class ChatsManagerTest : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(ChatsManagerTest);
CPPUNIT_TEST(testFirstOpenWindowIncoming);
CPPUNIT_TEST(testSecondOpenWindowIncoming);
CPPUNIT_TEST(testFirstOpenWindowOutgoing);
CPPUNIT_TEST(testFirstOpenWindowBareToFull);
CPPUNIT_TEST(testSecondWindow);
CPPUNIT_TEST(testUnbindRebind);
CPPUNIT_TEST(testNoDuplicateUnbind);
CPPUNIT_TEST(testThreeMUCWindows);
CPPUNIT_TEST(testChatControllerPresenceAccessUpdatedOnRemoveFromRoster);
CPPUNIT_TEST(testChatControllerPresenceAccessUpdatedOnAddToRoster);
CPPUNIT_TEST(testChatControllerPresenceAccessUpdatedOnSubscriptionChangeToBoth);
CPPUNIT_TEST(testChatControllerPresenceAccessUpdatedOnSubscriptionChangeToFrom);
CPPUNIT_TEST(testChatControllerFullJIDBindingOnMessageAndNotReceipt);
CPPUNIT_TEST(testChatControllerFullJIDBindingOnTypingAndNotActive);
CPPUNIT_TEST(testLocalMUCServiceDiscoveryResetOnDisconnect);
CPPUNIT_TEST(testPresenceChangeDoesNotReplaceMUCInvite);
+ CPPUNIT_TEST(testNotSplittingMUCPresenceJoinLeaveLinesOnChatStateNotifications);
// MUC PM Tests
CPPUNIT_TEST(testChatControllerPMPresenceHandling);
CPPUNIT_TEST(testChatControllerMucPmUnavailableErrorHandling);
// Highlighting tests
CPPUNIT_TEST(testChatControllerHighlightingNotificationTesting);
CPPUNIT_TEST(testChatControllerHighlightingNotificationDeduplicateSounds);
CPPUNIT_TEST(testChatControllerHighlightingNotificationKeyword);
CPPUNIT_TEST(testChatControllerMeMessageHandling);
CPPUNIT_TEST(testRestartingMUCComponentCrash);
CPPUNIT_TEST(testChatControllerMeMessageHandlingInMUC);
// Carbons tests
CPPUNIT_TEST(testCarbonsForwardedIncomingMessageToSecondResource);
CPPUNIT_TEST(testCarbonsForwardedOutgoingMessageFromSecondResource);
CPPUNIT_TEST_SUITE_END();
public:
void setUp() {
mocks_ = new MockRepository();
notifier_ = std::unique_ptr<DummyNotifier>(new DummyNotifier());
jid_ = JID("test@test.com/resource");
stanzaChannel_ = new DummyStanzaChannel();
iqRouter_ = new IQRouter(stanzaChannel_);
eventController_ = new EventController();
chatWindowFactory_ = mocks_->InterfaceMock<ChatWindowFactory>();
joinMUCWindowFactory_ = mocks_->InterfaceMock<JoinMUCWindowFactory>();
@@ -1068,60 +1069,127 @@ public:
MockChatWindow* window = new MockChatWindow();
mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window);
std::shared_ptr<Message> message(new Message());
message->setFrom(messageJID);
std::string body("This is a legible message. >HEH@)oeueu");
message->setBody(body);
manager_->handleIncomingMessage(message);
CPPUNIT_ASSERT_EQUAL(body, MockChatWindow::bodyFromMessage(window->lastAddedMessage_));
auto incomingMUCInvite = std::make_shared<Message>();
incomingMUCInvite->setFrom(messageJID);
auto invitePayload = std::make_shared<MUCInvitationPayload>();
invitePayload->setJID("room@muc.service.com");
incomingMUCInvite->addPayload(invitePayload);
stanzaChannel_->onPresenceReceived(generateIncomingPresence(Presence::Unavailable));
stanzaChannel_->onPresenceReceived(generateIncomingPresence(Presence::Available));
window->resetLastMessages();
manager_->handleIncomingMessage(incomingMUCInvite);
CPPUNIT_ASSERT_EQUAL(JID("room@muc.service.com"), window->lastMUCInvitationJID_);
stanzaChannel_->onPresenceReceived(generateIncomingPresence(Presence::Unavailable));
CPPUNIT_ASSERT_EQUAL(std::string(""), MockChatWindow::bodyFromMessage(window->lastReplacedMessage_));
CPPUNIT_ASSERT_EQUAL(std::string("testling@test.com has gone offline."), MockChatWindow::bodyFromMessage(window->lastAddedPresence_));
}
+ void testNotSplittingMUCPresenceJoinLeaveLinesOnChatStateNotifications() {
+ JID mucJID("mucroom@rooms.test.com");
+ std::string nickname = "toodles";
+
+ MockChatWindow* window = new MockChatWindow();
+ mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(mucJID, uiEventStream_).Return(window);
+
+ uiEventStream_->send(std::make_shared<JoinMUCUIEvent>(mucJID, boost::optional<std::string>(), nickname));
+
+ auto genRemoteMUCPresence = [=]() {
+ auto presence = Presence::create();
+ presence->setFrom(mucJID.withResource(nickname));
+ presence->setTo(jid_);
+ return presence;
+ };
+
+ {
+ auto presence = genRemoteMUCPresence();
+ auto userPayload = std::make_shared<MUCUserPayload>();
+ userPayload->addStatusCode(110);
+ userPayload->addItem(MUCItem(MUCOccupant::Owner, jid_, MUCOccupant::Moderator));
+ presence->addPayload(userPayload);
+ stanzaChannel_->onPresenceReceived(presence);
+ }
+
+ {
+ auto presence = genRemoteMUCPresence();
+ presence->setFrom(mucJID.withResource("someDifferentNickname"));
+ auto userPayload = std::make_shared<MUCUserPayload>();
+ presence->addPayload(userPayload);
+ stanzaChannel_->onPresenceReceived(presence);
+ }
+ CPPUNIT_ASSERT_EQUAL(std::string("someDifferentNickname has entered the room."), window->bodyFromMessage(window->lastAddedPresence_));
+ CPPUNIT_ASSERT_EQUAL(std::string(), window->bodyFromMessage(window->lastReplacedMessage_));
+ window->resetLastMessages();
+
+ {
+ auto presence = genRemoteMUCPresence();
+ presence->setFrom(mucJID.withResource("Romeo"));
+ auto userPayload = std::make_shared<MUCUserPayload>();
+ presence->addPayload(userPayload);
+ stanzaChannel_->onPresenceReceived(presence);
+ }
+ CPPUNIT_ASSERT_EQUAL(std::string(), window->bodyFromMessage(window->lastAddedPresence_));
+ CPPUNIT_ASSERT_EQUAL(std::string("someDifferentNickname and Romeo have entered the room"), window->bodyFromMessage(window->lastReplacedMessage_));
+ window->resetLastMessages();
+
+ {
+ auto message = std::make_shared<Message>();
+ message->setFrom(mucJID.withResource("Romeo"));
+ message->setTo(mucJID);
+ message->setType(Message::Groupchat);
+ message->addPayload(std::make_shared<ChatState>(ChatState::Composing));
+ manager_->handleIncomingMessage(message);
+ }
+
+ {
+ auto presence = genRemoteMUCPresence();
+ presence->setFrom(mucJID.withResource("Juliet"));
+ auto userPayload = std::make_shared<MUCUserPayload>();
+ presence->addPayload(userPayload);
+ stanzaChannel_->onPresenceReceived(presence);
+ }
+ CPPUNIT_ASSERT_EQUAL(std::string(), window->bodyFromMessage(window->lastAddedPresence_));
+ CPPUNIT_ASSERT_EQUAL(std::string("someDifferentNickname, Romeo and Juliet have entered the room"), window->bodyFromMessage(window->lastReplacedMessage_));
+ }
+
template <typename CarbonsType>
Message::ref createCarbonsMessage(std::shared_ptr<CarbonsType> carbons, std::shared_ptr<Message> forwardedMessage) {
auto messageWrapper = std::make_shared<Message>();
messageWrapper->setFrom(jid_.toBare());
messageWrapper->setTo(jid_);
messageWrapper->setType(Message::Chat);
messageWrapper->addPayload(carbons);
auto forwarded = std::make_shared<Forwarded>();
carbons->setForwarded(forwarded);
forwarded->setStanza(forwardedMessage);
return messageWrapper;
}
void testCarbonsForwardedIncomingMessageToSecondResource() {
JID messageJID("testling@test.com/resource1");
JID jid2 = jid_.toBare().withResource("someOtherResource");
MockChatWindow* window = new MockChatWindow();
mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window);
std::shared_ptr<Message> message(new Message());
message->setFrom(messageJID);
std::string body("This is a legible message. >HEH@)oeueu");
message->setBody(body);
manager_->handleIncomingMessage(message);
CPPUNIT_ASSERT_EQUAL(body, MockChatWindow::bodyFromMessage(window->lastAddedMessage_));
// incoming carbons message from another resource
{