diff options
Diffstat (limited to 'Swiften/Chat')
-rw-r--r-- | Swiften/Chat/ChatStateNotifier.cpp | 112 | ||||
-rw-r--r-- | Swiften/Chat/ChatStateNotifier.h | 85 | ||||
-rw-r--r-- | Swiften/Chat/ChatStateTracker.cpp | 40 | ||||
-rw-r--r-- | Swiften/Chat/ChatStateTracker.h | 35 | ||||
-rw-r--r-- | Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp | 328 |
5 files changed, 325 insertions, 275 deletions
diff --git a/Swiften/Chat/ChatStateNotifier.cpp b/Swiften/Chat/ChatStateNotifier.cpp index d507c4d..48a65ab 100644 --- a/Swiften/Chat/ChatStateNotifier.cpp +++ b/Swiften/Chat/ChatStateNotifier.cpp @@ -1,93 +1,117 @@ /* - * Copyright (c) 2010 Kevin Smith - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <Swiften/Chat/ChatStateNotifier.h> +#include <cassert> +#include <memory> + #include <boost/bind.hpp> -#include <boost/smart_ptr/make_shared.hpp> -#include <Swiften/Elements/Message.h> -#include <Swiften/Elements/ChatState.h> #include <Swiften/Client/StanzaChannel.h> #include <Swiften/Disco/EntityCapsProvider.h> +#include <Swiften/Elements/ChatState.h> +#include <Swiften/Elements/Message.h> +#include <Swiften/Network/Timer.h> +#include <Swiften/Network/TimerFactory.h> + namespace Swift { -ChatStateNotifier::ChatStateNotifier(StanzaChannel* stanzaChannel, const JID& contact, EntityCapsProvider* entityCapsManager) : stanzaChannel_(stanzaChannel), entityCapsManager_(entityCapsManager), contact_(contact) { - setContact(contact); - entityCapsManager_->onCapsChanged.connect(boost::bind(&ChatStateNotifier::handleCapsChanged, this, _1)); + +ChatStateNotifier::ChatStateNotifier(StanzaChannel* stanzaChannel, const JID& contact, EntityCapsProvider* entityCapsManager, TimerFactory* timerFactory, int idleTimeInMilliSecs) : stanzaChannel_(stanzaChannel), entityCapsManager_(entityCapsManager), contact_(contact) { + setContact(contact); + entityCapsManager_->onCapsChanged.connect(boost::bind(&ChatStateNotifier::handleCapsChanged, this, _1)); + assert(timerFactory); + idleTimer_ = timerFactory->createTimer(idleTimeInMilliSecs); + assert(!!idleTimer_); + idleTimer_->onTick.connect(boost::bind(&ChatStateNotifier::userBecameIdleWhileTyping, this)); } ChatStateNotifier::~ChatStateNotifier() { - entityCapsManager_->onCapsChanged.disconnect(boost::bind(&ChatStateNotifier::handleCapsChanged, this, _1)); + entityCapsManager_->onCapsChanged.disconnect(boost::bind(&ChatStateNotifier::handleCapsChanged, this, _1)); + idleTimer_->stop(); + idleTimer_->onTick.disconnect(boost::bind(&ChatStateNotifier::userBecameIdleWhileTyping, this)); } void ChatStateNotifier::setContact(const JID& contact) { - contactHasSentActive_ = false; - userIsTyping_ = false; - contactIsOnline_ = false; - contact_ = contact; - handleCapsChanged(contact_); + contactHasSentActive_ = false; + userIsTyping_ = false; + contactIsOnline_ = false; + contact_ = contact; + handleCapsChanged(contact_); } void ChatStateNotifier::setContactIsOnline(bool online) { - contactIsOnline_ = online; + contactIsOnline_ = online; } void ChatStateNotifier::setUserIsTyping() { - bool should = contactShouldReceiveStates(); - if (should && !userIsTyping_) { - userIsTyping_ = true; - changeState(ChatState::Composing); - } + idleTimer_->stop(); + bool should = contactShouldReceiveStates(); + if (should && !userIsTyping_) { + userIsTyping_ = true; + changeState(ChatState::Composing); + } + if (should) { + idleTimer_->start(); + } } void ChatStateNotifier::userSentMessage() { - userIsTyping_ = false; + idleTimer_->stop(); + userIsTyping_ = false; } void ChatStateNotifier::userCancelledNewMessage() { - if (userIsTyping_) { - userIsTyping_ = false; - changeState(ChatState::Active); - } + idleTimer_->stop(); + if (userIsTyping_) { + userIsTyping_ = false; + changeState(ChatState::Active); + } +} + +void ChatStateNotifier::userBecameIdleWhileTyping() { + // For now we are returning to active state. When support for the Paused, Inactive and Gone states + // is implemeted, this function should Implement the Pause/Inactive functionality. + userCancelledNewMessage(); } void ChatStateNotifier::receivedMessageFromContact(bool hasActiveElement) { - contactHasSentActive_ = hasActiveElement; + contactHasSentActive_ = hasActiveElement; } bool ChatStateNotifier::contactShouldReceiveStates() { - /* So, yes, the XEP says to look at caps, but it also says that once you've - heard from the contact, the active state overrides this. - *HOWEVER* it says that the MUST NOT send csn if you haven't received - active is OPTIONAL behaviour for if you haven't got caps.*/ - return contactIsOnline_ && (contactHasSentActive_ || contactHas85Caps_); + /* So, yes, the XEP says to look at caps, but it also says that once you've + heard from the contact, the active state overrides this. + *HOWEVER* it says that the MUST NOT send csn if you haven't received + active is OPTIONAL behaviour for if you haven't got caps.*/ + return contactIsOnline_ && (contactHasSentActive_ || contactHas85Caps_); } void ChatStateNotifier::changeState(ChatState::ChatStateType state) { - boost::shared_ptr<Message> message(boost::make_shared<Message>()); - message->setTo(contact_); - message->addPayload(boost::make_shared<ChatState>(state)); - stanzaChannel_->sendMessage(message); + std::shared_ptr<Message> message(std::make_shared<Message>()); + message->setTo(contact_); + message->addPayload(std::make_shared<ChatState>(state)); + stanzaChannel_->sendMessage(message); } void ChatStateNotifier::addChatStateRequest(Message::ref message) { - if (contactShouldReceiveStates()) { - message->addPayload(boost::make_shared<ChatState>(ChatState::Active)); - } + if (contactShouldReceiveStates()) { + message->addPayload(std::make_shared<ChatState>(ChatState::Active)); + } } void ChatStateNotifier::handleCapsChanged(const JID& jid) { - if (jid == contact_) { - DiscoInfo::ref caps = entityCapsManager_->getCaps(contact_); - bool hasCSN = caps && caps->hasFeature(DiscoInfo::ChatStatesFeature); - contactHas85Caps_ = hasCSN; - } + if (jid == contact_) { + DiscoInfo::ref caps = entityCapsManager_->getCaps(contact_); + bool hasCSN = caps && caps->hasFeature(DiscoInfo::ChatStatesFeature); + contactHas85Caps_ = hasCSN; + } } } diff --git a/Swiften/Chat/ChatStateNotifier.h b/Swiften/Chat/ChatStateNotifier.h index 5b99266..2b24c76 100644 --- a/Swiften/Chat/ChatStateNotifier.h +++ b/Swiften/Chat/ChatStateNotifier.h @@ -1,51 +1,58 @@ /* - * Copyright (c) 2010 Kevin Smith - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once -#include <Swiften/Base/boost_bsignals.h> -#include <boost/shared_ptr.hpp> +#include <memory> + +#include <boost/signals2.hpp> #include <Swiften/Base/API.h> -#include <Swiften/Elements/Message.h> #include <Swiften/Elements/ChatState.h> +#include <Swiften/Elements/Message.h> #include <Swiften/JID/JID.h> namespace Swift { - class StanzaChannel; - class EntityCapsProvider; - - class SWIFTEN_API ChatStateNotifier { - public: - ChatStateNotifier(StanzaChannel* stanzaChannel, const JID& contact, EntityCapsProvider* entityCapsManager); - ~ChatStateNotifier(); - - void setContact(const JID& contact); - - void addChatStateRequest(Message::ref message); - - void setUserIsTyping(); - void userSentMessage(); - void userCancelledNewMessage(); - - void receivedMessageFromContact(bool hasActiveElement); - void setContactIsOnline(bool online); - - private: - bool contactShouldReceiveStates(); - void changeState(ChatState::ChatStateType type); - void handleCapsChanged(const JID& contact); - - private: - StanzaChannel* stanzaChannel_; - EntityCapsProvider* entityCapsManager_; - JID contact_; - bool contactHas85Caps_; - bool contactHasSentActive_; - bool userIsTyping_; - bool contactIsOnline_; - }; + class StanzaChannel; + class EntityCapsProvider; + class TimerFactory; + class Timer; + + + class SWIFTEN_API ChatStateNotifier { + public: + ChatStateNotifier(StanzaChannel* stanzaChannel, const JID& contact, EntityCapsProvider* entityCapsManager, TimerFactory* timerFactory, int idleTimeInMilliSecs); + ~ChatStateNotifier(); + + void setContact(const JID& contact); + + void addChatStateRequest(Message::ref message); + + void setUserIsTyping(); + void userSentMessage(); + void userCancelledNewMessage(); + + void receivedMessageFromContact(bool hasActiveElement); + void setContactIsOnline(bool online); + + private: + void userBecameIdleWhileTyping(); + bool contactShouldReceiveStates(); + void changeState(ChatState::ChatStateType type); + void handleCapsChanged(const JID& contact); + + private: + StanzaChannel* stanzaChannel_; + EntityCapsProvider* entityCapsManager_; + JID contact_; + bool contactHas85Caps_; + bool contactHasSentActive_; + bool userIsTyping_; + bool contactIsOnline_; + std::shared_ptr<Timer> idleTimer_; + + }; } diff --git a/Swiften/Chat/ChatStateTracker.cpp b/Swiften/Chat/ChatStateTracker.cpp index 27be86c..839f47d 100644 --- a/Swiften/Chat/ChatStateTracker.cpp +++ b/Swiften/Chat/ChatStateTracker.cpp @@ -1,37 +1,37 @@ /* - * Copyright (c) 2010 Kevin Smith - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <Swiften/Chat/ChatStateTracker.h> namespace Swift { ChatStateTracker::ChatStateTracker() { - currentState_ = ChatState::Gone; + currentState_ = ChatState::Gone; } -void ChatStateTracker::handleMessageReceived(boost::shared_ptr<Message> message) { - if (message->getType() == Message::Error) { - return; - } - boost::shared_ptr<ChatState> statePayload = message->getPayload<ChatState>(); - if (statePayload) { - changeState(statePayload->getChatState());; - } +void ChatStateTracker::handleMessageReceived(std::shared_ptr<Message> message) { + if (message->getType() == Message::Error) { + return; + } + std::shared_ptr<ChatState> statePayload = message->getPayload<ChatState>(); + if (statePayload) { + changeState(statePayload->getChatState()); + } } -void ChatStateTracker::handlePresenceChange(boost::shared_ptr<Presence> newPresence) { - if (newPresence->getType() == Presence::Unavailable) { - onChatStateChange(ChatState::Gone); - } +void ChatStateTracker::handlePresenceChange(std::shared_ptr<Presence> newPresence) { + if (newPresence->getType() == Presence::Unavailable) { + onChatStateChange(ChatState::Gone); + } } void ChatStateTracker::changeState(ChatState::ChatStateType state) { - if (state != currentState_) { - currentState_ = state; - onChatStateChange(state); - } + if (state != currentState_) { + currentState_ = state; + onChatStateChange(state); + } } } diff --git a/Swiften/Chat/ChatStateTracker.h b/Swiften/Chat/ChatStateTracker.h index e401b0a..c903e3a 100644 --- a/Swiften/Chat/ChatStateTracker.h +++ b/Swiften/Chat/ChatStateTracker.h @@ -1,28 +1,29 @@ /* - * Copyright (c) 2010 Kevin Smith - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once -#include <Swiften/Base/boost_bsignals.h> -#include <boost/shared_ptr.hpp> -#include <Swiften/Base/API.h> +#include <memory> + +#include <boost/signals2.hpp> +#include <Swiften/Base/API.h> +#include <Swiften/Elements/ChatState.h> #include <Swiften/Elements/Message.h> #include <Swiften/Elements/Presence.h> -#include <Swiften/Elements/ChatState.h> namespace Swift { - class SWIFTEN_API ChatStateTracker { - public: - ChatStateTracker(); - void handleMessageReceived(boost::shared_ptr<Message> message); - void handlePresenceChange(boost::shared_ptr<Presence> newPresence); - boost::signal<void (ChatState::ChatStateType)> onChatStateChange; - private: - void changeState(ChatState::ChatStateType state); - ChatState::ChatStateType currentState_; - }; + class SWIFTEN_API ChatStateTracker { + public: + ChatStateTracker(); + void handleMessageReceived(std::shared_ptr<Message> message); + void handlePresenceChange(std::shared_ptr<Presence> newPresence); + boost::signals2::signal<void (ChatState::ChatStateType)> onChatStateChange; + private: + void changeState(ChatState::ChatStateType state); + ChatState::ChatStateType currentState_; + }; } diff --git a/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp b/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp index 1b91076..b6c909a 100644 --- a/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp +++ b/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp @@ -1,168 +1,186 @@ /* - * Copyright (c) 2010 Kevin Smith - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ -#include <cppunit/extensions/HelperMacros.h> -#include <cppunit/extensions/TestFactoryRegistry.h> #include <boost/bind.hpp> -#include <Swiften/Base/foreach.h> +#include <gtest/gtest.h> + #include <Swiften/Chat/ChatStateNotifier.h> #include <Swiften/Client/DummyStanzaChannel.h> #include <Swiften/Disco/DummyEntityCapsProvider.h> +#include <Swiften/Network/DummyTimerFactory.h> + +// Clang wrongly things that tests for 0 are using 0 as null. +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" using namespace Swift; -class ChatStateNotifierTest : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(ChatStateNotifierTest); - CPPUNIT_TEST(testStartTypingReply_CapsNotIncluded); - CPPUNIT_TEST(testStartTypingReply_CapsIncluded); - CPPUNIT_TEST(testCancelledNewMessage); - CPPUNIT_TEST(testContinueTypingReply_CapsIncluded); - CPPUNIT_TEST(testTypeReplies_WentOffline); - CPPUNIT_TEST(testContactShouldReceiveStates_CapsOnly); - CPPUNIT_TEST(testContactShouldReceiveStates_CapsNorActive); - CPPUNIT_TEST(testContactShouldReceiveStates_ActiveOverrideOn); - CPPUNIT_TEST(testContactShouldReceiveStates_ActiveOverrideOff); - CPPUNIT_TEST_SUITE_END(); - -public: - void setUp() { - stanzaChannel = new DummyStanzaChannel(); - stanzaChannel->setAvailable(true); - entityCapsProvider = new DummyEntityCapsProvider(); - notifier_ = new ChatStateNotifier(stanzaChannel, JID("foo@bar.com/baz"), entityCapsProvider); - notifier_->setContactIsOnline(true); - } - - void tearDown() { - delete notifier_; - delete entityCapsProvider; - delete stanzaChannel; - } - - void testStartTypingReply_CapsNotIncluded() { - notifier_->setUserIsTyping(); - CPPUNIT_ASSERT_EQUAL(0, getComposingCount()); - } - - void testSendTwoMessages() { - setContactHas85Caps(); - notifier_->setUserIsTyping(); - notifier_->userSentMessage(); - notifier_->setUserIsTyping(); - notifier_->userSentMessage(); - CPPUNIT_ASSERT_EQUAL(2, getComposingCount()); - } - - void testCancelledNewMessage() { - setContactHas85Caps(); - notifier_->setUserIsTyping(); - notifier_->userCancelledNewMessage(); - CPPUNIT_ASSERT_EQUAL(1, getComposingCount()); - CPPUNIT_ASSERT_EQUAL(1, getActiveCount()); - CPPUNIT_ASSERT_EQUAL(ChatState::Active, stanzaChannel->sentStanzas[stanzaChannel->sentStanzas.size()-1]->getPayload<ChatState>()->getChatState()); - } - - - void testContactShouldReceiveStates_CapsOnly() { - setContactHas85Caps(); - boost::shared_ptr<Message> message(new Message()); - notifier_->addChatStateRequest(message); - CPPUNIT_ASSERT(message->getPayload<ChatState>()); - CPPUNIT_ASSERT_EQUAL(ChatState::Active, message->getPayload<ChatState>()->getChatState()); - } - - void testContactShouldReceiveStates_CapsNorActive() { - boost::shared_ptr<Message> message(new Message()); - notifier_->addChatStateRequest(message); - CPPUNIT_ASSERT(!message->getPayload<ChatState>()); - } - - void testContactShouldReceiveStates_ActiveOverrideOn() { - notifier_->receivedMessageFromContact(true); - boost::shared_ptr<Message> message(new Message()); - notifier_->addChatStateRequest(message); - CPPUNIT_ASSERT(message->getPayload<ChatState>()); - CPPUNIT_ASSERT_EQUAL(ChatState::Active, message->getPayload<ChatState>()->getChatState()); - } - - void testContactShouldReceiveStates_ActiveOverrideOff() { - setContactHas85Caps(); - notifier_->receivedMessageFromContact(false); - /* I originally read the MUST NOT send after receiving without Active and - * thought this should check for false, but I later found it was OPTIONAL - * (MAY) behaviour only for if you didn't receive caps. - */ - boost::shared_ptr<Message> message(new Message()); - notifier_->addChatStateRequest(message); - CPPUNIT_ASSERT(message->getPayload<ChatState>()); - CPPUNIT_ASSERT_EQUAL(ChatState::Active, message->getPayload<ChatState>()->getChatState()); - } - - - void testStartTypingReply_CapsIncluded() { - setContactHas85Caps(); - notifier_->setUserIsTyping(); - CPPUNIT_ASSERT_EQUAL(1, getComposingCount()); - } - - void testContinueTypingReply_CapsIncluded() { - setContactHas85Caps(); - notifier_->setUserIsTyping(); - notifier_->setUserIsTyping(); - notifier_->setUserIsTyping(); - CPPUNIT_ASSERT_EQUAL(1, getComposingCount()); - notifier_->userSentMessage(); - notifier_->setUserIsTyping(); - CPPUNIT_ASSERT_EQUAL(2, getComposingCount()); - - } - - void testTypeReplies_WentOffline() { - setContactHas85Caps(); - notifier_->setUserIsTyping(); - CPPUNIT_ASSERT_EQUAL(1, getComposingCount()); - notifier_->setContactIsOnline(false); - notifier_->userSentMessage(); - notifier_->setUserIsTyping(); - CPPUNIT_ASSERT_EQUAL(1, getComposingCount()); - } - - private: - void setContactHas85Caps() { - DiscoInfo::ref caps(new DiscoInfo()); - caps->addFeature(DiscoInfo::ChatStatesFeature); - entityCapsProvider->caps[JID("foo@bar.com/baz")] = caps; - entityCapsProvider->onCapsChanged(JID("foo@bar.com/baz")); - } - - int getComposingCount() const { - int result = 0; - foreach(boost::shared_ptr<Stanza> stanza, stanzaChannel->sentStanzas) { - if (stanza->getPayload<ChatState>() && stanza->getPayload<ChatState>()->getChatState() == ChatState::Composing) { - result++; - } - } - return result; - } - - int getActiveCount() const { - int result = 0; - foreach(boost::shared_ptr<Stanza> stanza, stanzaChannel->sentStanzas) { - if (stanza->getPayload<ChatState>() && stanza->getPayload<ChatState>()->getChatState() == ChatState::Active) { - result++; - } - } - return result; - } - - private: - DummyStanzaChannel* stanzaChannel; - DummyEntityCapsProvider* entityCapsProvider; - ChatStateNotifier* notifier_; +class ChatStateNotifierTest : public ::testing::Test { + +protected: + virtual void SetUp() { + stanzaChannel = new DummyStanzaChannel(); + stanzaChannel->setAvailable(true); + entityCapsProvider = new DummyEntityCapsProvider(); + timerFactory_ = new DummyTimerFactory(); + notifier_ = new ChatStateNotifier(stanzaChannel, JID("foo@bar.com/baz"), entityCapsProvider, timerFactory_, 2); + notifier_->setContactIsOnline(true); + } + + virtual void TearDown() { + delete notifier_; + delete timerFactory_; + delete entityCapsProvider; + delete stanzaChannel; + } + + void setContactHas85Caps() { + DiscoInfo::ref caps(new DiscoInfo()); + caps->addFeature(DiscoInfo::ChatStatesFeature); + entityCapsProvider->caps[JID("foo@bar.com/baz")] = caps; + entityCapsProvider->onCapsChanged(JID("foo@bar.com/baz")); + } + + int getComposingCount() const { + int result = 0; + for (auto&& stanza : stanzaChannel->sentStanzas) { + if (stanza->getPayload<ChatState>() && stanza->getPayload<ChatState>()->getChatState() == ChatState::Composing) { + result++; + } + } + return result; + } + + int getActiveCount() const { + int result = 0; + for (auto&& stanza : stanzaChannel->sentStanzas) { + if (stanza->getPayload<ChatState>() && stanza->getPayload<ChatState>()->getChatState() == ChatState::Active) { + result++; + } + } + return result; + } + + DummyStanzaChannel* stanzaChannel; + DummyEntityCapsProvider* entityCapsProvider; + DummyTimerFactory* timerFactory_; + ChatStateNotifier* notifier_; }; -CPPUNIT_TEST_SUITE_REGISTRATION(ChatStateNotifierTest); +TEST_F(ChatStateNotifierTest, testStartTypingReply_CapsNotIncluded) { + notifier_->setUserIsTyping(); + ASSERT_EQ(0, getComposingCount()); +} + +TEST_F(ChatStateNotifierTest, testSendTwoMessages) { + setContactHas85Caps(); + notifier_->setUserIsTyping(); + notifier_->userSentMessage(); + notifier_->setUserIsTyping(); + notifier_->userSentMessage(); + ASSERT_EQ(2, getComposingCount()); +} + +TEST_F(ChatStateNotifierTest, testCancelledNewMessage) { + setContactHas85Caps(); + notifier_->setUserIsTyping(); + notifier_->userCancelledNewMessage(); + ASSERT_EQ(1, getComposingCount()); + ASSERT_EQ(1, getActiveCount()); + ASSERT_EQ(ChatState::Active, stanzaChannel->sentStanzas[stanzaChannel->sentStanzas.size() - 1]->getPayload<ChatState>()->getChatState()); +} + +TEST_F(ChatStateNotifierTest, testIdleWhileTypingNewMessage) { + setContactHas85Caps(); + //The channel should be empty + ASSERT_EQ(0, getComposingCount()); + ASSERT_EQ(0, getActiveCount()); + notifier_->setUserIsTyping(); + timerFactory_->setTime(1); + //1 Composing stanza is expected + ASSERT_EQ(1, getComposingCount()); + ASSERT_EQ(0, getActiveCount()); + timerFactory_->setTime(2); + //The idleTimer period has expired, the channel should have 1 composing and 1 active status stanza + ASSERT_EQ(1, getComposingCount()); + ASSERT_EQ(1, getActiveCount()); + ASSERT_EQ(ChatState::Active, stanzaChannel->sentStanzas[stanzaChannel->sentStanzas.size() - 1]->getPayload<ChatState>()->getChatState()); + timerFactory_->setTime(4); + //At the second tick no further state stanzas should be sent. + ASSERT_EQ(1, getComposingCount()); + ASSERT_EQ(1, getActiveCount()); + ASSERT_EQ(ChatState::Active, stanzaChannel->sentStanzas[stanzaChannel->sentStanzas.size() - 1]->getPayload<ChatState>()->getChatState()); +} + +TEST_F(ChatStateNotifierTest, testIdleWhileTypingNewMessageNoCaps) { + notifier_->setUserIsTyping(); + timerFactory_->setTime(3); + ASSERT_EQ(0, getComposingCount()); + ASSERT_EQ(0, getActiveCount()); +} +TEST_F(ChatStateNotifierTest, testContactShouldReceiveStates_CapsOnly) { + setContactHas85Caps(); + std::shared_ptr<Message> message(new Message()); + notifier_->addChatStateRequest(message); + EXPECT_TRUE(message->getPayload<ChatState>()); + ASSERT_EQ(ChatState::Active, message->getPayload<ChatState>()->getChatState()); +} + +TEST_F(ChatStateNotifierTest, testContactShouldReceiveStates_CapsNorActive) { + std::shared_ptr<Message> message(new Message()); + notifier_->addChatStateRequest(message); + EXPECT_TRUE(!message->getPayload<ChatState>()); +} + +TEST_F(ChatStateNotifierTest, testContactShouldReceiveStates_ActiveOverrideOn) { + notifier_->receivedMessageFromContact(true); + std::shared_ptr<Message> message(new Message()); + notifier_->addChatStateRequest(message); + EXPECT_TRUE(message->getPayload<ChatState>()); + ASSERT_EQ(ChatState::Active, message->getPayload<ChatState>()->getChatState()); +} + +TEST_F(ChatStateNotifierTest, testContactShouldReceiveStates_ActiveOverrideOff) { + setContactHas85Caps(); + notifier_->receivedMessageFromContact(false); + /* I originally read the MUST NOT send after receiving without Active and + * thought this should check for false, but I later found it was OPTIONAL + * (MAY) behaviour only for if you didn't receive caps. + */ + std::shared_ptr<Message> message(new Message()); + notifier_->addChatStateRequest(message); + EXPECT_TRUE(message->getPayload<ChatState>()); + ASSERT_EQ(ChatState::Active, message->getPayload<ChatState>()->getChatState()); +} + + +TEST_F(ChatStateNotifierTest, testStartTypingReply_CapsIncluded) { + setContactHas85Caps(); + notifier_->setUserIsTyping(); + ASSERT_EQ(1, getComposingCount()); +} + +TEST_F(ChatStateNotifierTest, testContinueTypingReply_CapsIncluded) { + setContactHas85Caps(); + notifier_->setUserIsTyping(); + notifier_->setUserIsTyping(); + notifier_->setUserIsTyping(); + ASSERT_EQ(1, getComposingCount()); + notifier_->userSentMessage(); + notifier_->setUserIsTyping(); + ASSERT_EQ(2, getComposingCount()); + +} + +TEST_F(ChatStateNotifierTest, testTypeReplies_WentOffline) { + setContactHas85Caps(); + notifier_->setUserIsTyping(); + ASSERT_EQ(1, getComposingCount()); + notifier_->setContactIsOnline(false); + notifier_->userSentMessage(); + notifier_->setUserIsTyping(); + ASSERT_EQ(1, getComposingCount()); +} |