mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
refactor(messages): Create class to manage sending/receiving friend messages from core
This commit is contained in:
parent
678fc51c1b
commit
22a4c38bfd
@ -326,6 +326,11 @@ set(${PROJECT_NAME}_SOURCES
|
||||
src/model/contact.h
|
||||
src/model/friend.cpp
|
||||
src/model/friend.h
|
||||
src/model/message.h
|
||||
src/model/message.cpp
|
||||
src/model/imessagedispatcher.h
|
||||
src/model/friendmessagedispatcher.h
|
||||
src/model/friendmessagedispatcher.cpp
|
||||
src/model/groupinvite.cpp
|
||||
src/model/groupinvite.h
|
||||
src/model/group.cpp
|
||||
|
@ -28,6 +28,7 @@ auto_test(net bsu)
|
||||
auto_test(persistence paths)
|
||||
auto_test(persistence dbschema)
|
||||
auto_test(persistence offlinemsgengine)
|
||||
auto_test(model friendmessagedispatcher)
|
||||
|
||||
if (UNIX)
|
||||
auto_test(platform posixsignalnotifier)
|
||||
|
@ -28,6 +28,7 @@
|
||||
class ICoreFriendMessageSender
|
||||
{
|
||||
public:
|
||||
virtual ~ICoreFriendMessageSender() = default;
|
||||
virtual bool sendAction(uint32_t friendId, const QString& action, ReceiptNum& receipt) = 0;
|
||||
virtual bool sendMessage(uint32_t friendId, const QString& message, ReceiptNum& receipt) = 0;
|
||||
};
|
||||
|
121
src/model/friendmessagedispatcher.cpp
Normal file
121
src/model/friendmessagedispatcher.cpp
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
Copyright © 2019 by The qTox Project Contributors
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
qTox is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
qTox is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "friendmessagedispatcher.h"
|
||||
#include "src/persistence/settings.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* @brief Sends message to friend using messageSender
|
||||
* @param[in] messageSender
|
||||
* @param[in] f
|
||||
* @param[in] message
|
||||
* @param[out] receipt
|
||||
*/
|
||||
bool sendMessageToCore(ICoreFriendMessageSender& messageSender, const Friend& f,
|
||||
const Message& message, ReceiptNum& receipt)
|
||||
{
|
||||
uint32_t friendId = f.getId();
|
||||
|
||||
auto sendFn = message.isAction ? std::mem_fn(&ICoreFriendMessageSender::sendAction)
|
||||
: std::mem_fn(&ICoreFriendMessageSender::sendMessage);
|
||||
|
||||
return sendFn(messageSender, friendId, message.content, receipt);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
FriendMessageDispatcher::FriendMessageDispatcher(Friend& f_, ICoreFriendMessageSender& messageSender_)
|
||||
: f(f_)
|
||||
, messageSender(messageSender_)
|
||||
, offlineMsgEngine(&f_, &messageSender_)
|
||||
{
|
||||
connect(&f, &Friend::statusChanged, this, &FriendMessageDispatcher::onFriendStatusChange);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IMessageSender::sendMessage
|
||||
*/
|
||||
std::pair<DispatchedMessageId, DispatchedMessageId>
|
||||
FriendMessageDispatcher::sendMessage(bool isAction, const QString& content)
|
||||
{
|
||||
const auto firstId = nextMessageId;
|
||||
auto lastId = nextMessageId;
|
||||
for (const auto& message : processOutgoingMessage(isAction, content)) {
|
||||
auto messageId = nextMessageId++;
|
||||
lastId = messageId;
|
||||
auto onOfflineMsgComplete = [this, messageId] { emit this->messageComplete(messageId); };
|
||||
|
||||
ReceiptNum receipt;
|
||||
|
||||
bool messageSent = false;
|
||||
|
||||
if (f.isOnline()) {
|
||||
messageSent = sendMessageToCore(messageSender, f, message, receipt);
|
||||
}
|
||||
|
||||
if (!messageSent) {
|
||||
offlineMsgEngine.addUnsentMessage(message, onOfflineMsgComplete);
|
||||
} else {
|
||||
offlineMsgEngine.addSentMessage(receipt, message, onOfflineMsgComplete);
|
||||
}
|
||||
|
||||
emit this->messageSent(messageId, message);
|
||||
}
|
||||
return std::make_pair(firstId, lastId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handles received message from toxcore
|
||||
* @param[in] isAction True if action message
|
||||
* @param[in] content Unprocessed toxcore message
|
||||
*/
|
||||
void FriendMessageDispatcher::onMessageReceived(bool isAction, const QString& content)
|
||||
{
|
||||
emit this->messageReceived(f.getPublicKey(), processIncomingMessage(isAction, content));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handles received receipt from toxcore
|
||||
* @param[in] receipt receipt id
|
||||
*/
|
||||
void FriendMessageDispatcher::onReceiptReceived(ReceiptNum receipt)
|
||||
{
|
||||
offlineMsgEngine.onReceiptReceived(receipt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handles status change for friend
|
||||
* @note Parameters just to fit slot api
|
||||
*/
|
||||
void FriendMessageDispatcher::onFriendStatusChange(const ToxPk&, Status::Status)
|
||||
{
|
||||
if (f.isOnline()) {
|
||||
offlineMsgEngine.deliverOfflineMsgs();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clears all currently outgoing messages
|
||||
*/
|
||||
void FriendMessageDispatcher::clearOutgoingMessages()
|
||||
{
|
||||
offlineMsgEngine.removeAllMessages();
|
||||
}
|
57
src/model/friendmessagedispatcher.h
Normal file
57
src/model/friendmessagedispatcher.h
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
Copyright © 2019 by The qTox Project Contributors
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
qTox is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
qTox is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef FRIEND_MESSAGE_DISPATCHER_H
|
||||
#define FRIEND_MESSAGE_DISPATCHER_H
|
||||
|
||||
#include "src/core/icorefriendmessagesender.h"
|
||||
#include "src/model/friend.h"
|
||||
#include "src/model/imessagedispatcher.h"
|
||||
#include "src/model/message.h"
|
||||
#include "src/persistence/offlinemsgengine.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class FriendMessageDispatcher : public IMessageDispatcher
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
FriendMessageDispatcher(Friend& f, ICoreFriendMessageSender& messageSender);
|
||||
|
||||
std::pair<DispatchedMessageId, DispatchedMessageId> sendMessage(bool isAction,
|
||||
const QString& content) override;
|
||||
void onMessageReceived(bool isAction, const QString& content);
|
||||
void onReceiptReceived(ReceiptNum receipt);
|
||||
void clearOutgoingMessages();
|
||||
private slots:
|
||||
void onFriendStatusChange(const ToxPk& key, Status::Status status);
|
||||
|
||||
private:
|
||||
Friend& f;
|
||||
DispatchedMessageId nextMessageId = DispatchedMessageId(0);
|
||||
|
||||
ICoreFriendMessageSender& messageSender;
|
||||
OfflineMsgEngine offlineMsgEngine;
|
||||
};
|
||||
|
||||
|
||||
#endif /* IMESSAGE_DISPATCHER_H */
|
68
src/model/imessagedispatcher.h
Normal file
68
src/model/imessagedispatcher.h
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
Copyright © 2019 by The qTox Project Contributors
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
qTox is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
qTox is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef IMESSAGE_DISPATCHER_H
|
||||
#define IMESSAGE_DISPATCHER_H
|
||||
|
||||
#include "src/model/friend.h"
|
||||
#include "src/model/message.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
using DispatchedMessageId = NamedType<size_t, struct SentMessageIdTag, Orderable, Incrementable>;
|
||||
Q_DECLARE_METATYPE(DispatchedMessageId);
|
||||
|
||||
class IMessageDispatcher : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
virtual ~IMessageDispatcher() = default;
|
||||
|
||||
/**
|
||||
* @brief Sends message to associated chat
|
||||
* @param[in] isAction True if is action message
|
||||
* @param[in] content Message content
|
||||
* @return Pair of first and last dispatched message IDs
|
||||
*/
|
||||
virtual std::pair<DispatchedMessageId, DispatchedMessageId>
|
||||
sendMessage(bool isAction, const QString& content) = 0;
|
||||
signals:
|
||||
/**
|
||||
* @brief Emitted when a message is received and processed
|
||||
*/
|
||||
void messageReceived(const ToxPk& sender, const Message& message);
|
||||
|
||||
/**
|
||||
* @brief Emitted when a message is processed and sent
|
||||
* @param id message id for completion
|
||||
* @param message sent message
|
||||
*/
|
||||
void messageSent(DispatchedMessageId id, const Message& message);
|
||||
|
||||
/**
|
||||
* @brief Emitted when a receiver report is received from the associated chat
|
||||
* @param id Id of message that is completed
|
||||
*/
|
||||
void messageComplete(DispatchedMessageId id);
|
||||
};
|
||||
|
||||
#endif /* IMESSAGE_DISPATCHER_H */
|
51
src/model/message.cpp
Normal file
51
src/model/message.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
Copyright © 2019 by The qTox Project Contributors
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
qTox is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
qTox is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "message.h"
|
||||
#include "src/core/core.h"
|
||||
|
||||
std::vector<Message> processOutgoingMessage(bool isAction, const QString& content)
|
||||
{
|
||||
std::vector<Message> ret;
|
||||
|
||||
QStringList splitMsgs = Core::splitMessage(content, tox_max_message_length());
|
||||
ret.reserve(splitMsgs.size());
|
||||
|
||||
QDateTime timestamp = QDateTime::currentDateTime();
|
||||
std::transform(splitMsgs.begin(), splitMsgs.end(), std::back_inserter(ret),
|
||||
[&](const QString& part) {
|
||||
Message message;
|
||||
message.isAction = isAction;
|
||||
message.content = part;
|
||||
message.timestamp = timestamp;
|
||||
return message;
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Message processIncomingMessage(bool isAction, const QString& message)
|
||||
{
|
||||
QDateTime timestamp = QDateTime::currentDateTime();
|
||||
auto ret = Message{};
|
||||
ret.isAction = isAction;
|
||||
ret.content = message;
|
||||
ret.timestamp = timestamp;
|
||||
return ret;
|
||||
}
|
@ -23,6 +23,8 @@
|
||||
#include <QDateTime>
|
||||
#include <QString>
|
||||
|
||||
#include <vector>
|
||||
|
||||
struct Message
|
||||
{
|
||||
bool isAction;
|
||||
@ -30,4 +32,8 @@ struct Message
|
||||
QDateTime timestamp;
|
||||
};
|
||||
|
||||
|
||||
std::vector<Message> processOutgoingMessage(bool isAction, const QString& content);
|
||||
Message processIncomingMessage(bool isAction, const QString& message);
|
||||
|
||||
#endif /*MESSAGE_H*/
|
||||
|
@ -28,6 +28,24 @@ struct Addable
|
||||
T operator+(T const& other) const { return static_cast<T const&>(*this).get() + other.get(); };
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct Incrementable
|
||||
{
|
||||
T& operator++()
|
||||
{
|
||||
auto& underlying = static_cast<T&>(*this).get();
|
||||
++underlying;
|
||||
return static_cast<T&>(*this);
|
||||
}
|
||||
|
||||
T operator++(int)
|
||||
{
|
||||
auto ret = T(static_cast<T const&>(*this));
|
||||
++(*this);
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct EqualityComparible
|
||||
{
|
||||
|
207
test/model/friendmessagedispatcher_test.cpp
Normal file
207
test/model/friendmessagedispatcher_test.cpp
Normal file
@ -0,0 +1,207 @@
|
||||
#include "src/core/icorefriendmessagesender.h"
|
||||
#include "src/model/friend.h"
|
||||
#include "src/model/friendmessagedispatcher.h"
|
||||
#include "src/model/message.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
#include <deque>
|
||||
|
||||
|
||||
class MockFriendMessageSender : public ICoreFriendMessageSender
|
||||
{
|
||||
public:
|
||||
bool sendAction(uint32_t friendId, const QString& action, ReceiptNum& receipt) override
|
||||
{
|
||||
if (canSend) {
|
||||
numSentActions++;
|
||||
receipt = receiptNum;
|
||||
receiptNum.get() += 1;
|
||||
}
|
||||
return canSend;
|
||||
}
|
||||
|
||||
bool sendMessage(uint32_t friendId, const QString& message, ReceiptNum& receipt) override
|
||||
{
|
||||
if (canSend) {
|
||||
numSentMessages++;
|
||||
receipt = receiptNum;
|
||||
receiptNum.get() += 1;
|
||||
}
|
||||
return canSend;
|
||||
}
|
||||
|
||||
bool canSend = true;
|
||||
ReceiptNum receiptNum{0};
|
||||
size_t numSentActions = 0;
|
||||
size_t numSentMessages = 0;
|
||||
};
|
||||
class TestFriendMessageDispatcher : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TestFriendMessageDispatcher();
|
||||
|
||||
private slots:
|
||||
void init();
|
||||
void testSignals();
|
||||
void testMessageSending();
|
||||
void testOfflineMessages();
|
||||
void testFailedMessage();
|
||||
|
||||
void onMessageSent(DispatchedMessageId id, Message message)
|
||||
{
|
||||
auto it = outgoingMessages.find(id);
|
||||
QVERIFY(it == outgoingMessages.end());
|
||||
outgoingMessages.emplace(id, std::move(message));
|
||||
}
|
||||
|
||||
void onMessageComplete(DispatchedMessageId id)
|
||||
{
|
||||
auto it = outgoingMessages.find(id);
|
||||
QVERIFY(it != outgoingMessages.end());
|
||||
outgoingMessages.erase(it);
|
||||
}
|
||||
|
||||
void onMessageReceived(const ToxPk& sender, Message message)
|
||||
{
|
||||
receivedMessages.push_back(std::move(message));
|
||||
}
|
||||
|
||||
private:
|
||||
// All unique_ptrs to make construction/init() easier to manage
|
||||
std::unique_ptr<Friend> f;
|
||||
std::unique_ptr<MockFriendMessageSender> messageSender;
|
||||
std::unique_ptr<FriendMessageDispatcher> friendMessageDispatcher;
|
||||
std::map<DispatchedMessageId, Message> outgoingMessages;
|
||||
std::deque<Message> receivedMessages;
|
||||
};
|
||||
|
||||
TestFriendMessageDispatcher::TestFriendMessageDispatcher() {}
|
||||
|
||||
/**
|
||||
* @brief Test initialization. Resets all member variables for a fresh test state
|
||||
*/
|
||||
void TestFriendMessageDispatcher::init()
|
||||
{
|
||||
f = std::unique_ptr<Friend>(new Friend(0, ToxPk()));
|
||||
f->setStatus(Status::Status::Online);
|
||||
messageSender = std::unique_ptr<MockFriendMessageSender>(new MockFriendMessageSender());
|
||||
friendMessageDispatcher =
|
||||
std::unique_ptr<FriendMessageDispatcher>(new FriendMessageDispatcher(*f, *messageSender));
|
||||
|
||||
connect(friendMessageDispatcher.get(), &FriendMessageDispatcher::messageSent, this,
|
||||
&TestFriendMessageDispatcher::onMessageSent);
|
||||
connect(friendMessageDispatcher.get(), &FriendMessageDispatcher::messageComplete, this,
|
||||
&TestFriendMessageDispatcher::onMessageComplete);
|
||||
connect(friendMessageDispatcher.get(), &FriendMessageDispatcher::messageReceived, this,
|
||||
&TestFriendMessageDispatcher::onMessageReceived);
|
||||
|
||||
outgoingMessages = std::map<DispatchedMessageId, Message>();
|
||||
receivedMessages = std::deque<Message>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Tests that the signals emitted by the dispatcher are all emitted at the correct times
|
||||
*/
|
||||
void TestFriendMessageDispatcher::testSignals()
|
||||
{
|
||||
auto startReceiptNum = messageSender->receiptNum;
|
||||
auto sentIds = friendMessageDispatcher->sendMessage(false, "test");
|
||||
auto endReceiptNum = messageSender->receiptNum;
|
||||
|
||||
// We should have received some message ids in our callbacks
|
||||
QVERIFY(sentIds.first == sentIds.second);
|
||||
QVERIFY(outgoingMessages.find(sentIds.first) != outgoingMessages.end());
|
||||
QVERIFY(startReceiptNum.get() != endReceiptNum.get());
|
||||
QVERIFY(outgoingMessages.size() == 1);
|
||||
|
||||
QVERIFY(outgoingMessages.begin()->second.isAction == false);
|
||||
QVERIFY(outgoingMessages.begin()->second.content == "test");
|
||||
|
||||
for (auto i = startReceiptNum; i < endReceiptNum; ++i.get()) {
|
||||
friendMessageDispatcher->onReceiptReceived(i);
|
||||
}
|
||||
|
||||
// If our completion ids were hooked up right this should be empty
|
||||
QVERIFY(outgoingMessages.empty());
|
||||
|
||||
// If signals are emitted correctly we should have one message in our received message buffer
|
||||
QVERIFY(receivedMessages.empty());
|
||||
friendMessageDispatcher->onMessageReceived(false, "test2");
|
||||
|
||||
QVERIFY(!receivedMessages.empty());
|
||||
QVERIFY(receivedMessages.front().isAction == false);
|
||||
QVERIFY(receivedMessages.front().content == "test2");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Tests that sent messages actually go through to core
|
||||
*/
|
||||
void TestFriendMessageDispatcher::testMessageSending()
|
||||
{
|
||||
friendMessageDispatcher->sendMessage(false, "Test");
|
||||
|
||||
QVERIFY(messageSender->numSentMessages == 1);
|
||||
QVERIFY(messageSender->numSentActions == 0);
|
||||
|
||||
friendMessageDispatcher->sendMessage(true, "Test");
|
||||
|
||||
QVERIFY(messageSender->numSentMessages == 1);
|
||||
QVERIFY(messageSender->numSentActions == 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Tests that messages dispatched while a friend is offline are sent later
|
||||
*/
|
||||
void TestFriendMessageDispatcher::testOfflineMessages()
|
||||
{
|
||||
f->setStatus(Status::Status::Offline);
|
||||
auto firstReceipt = messageSender->receiptNum;
|
||||
|
||||
friendMessageDispatcher->sendMessage(false, "test");
|
||||
friendMessageDispatcher->sendMessage(false, "test2");
|
||||
friendMessageDispatcher->sendMessage(true, "test3");
|
||||
|
||||
QVERIFY(messageSender->numSentActions == 0);
|
||||
QVERIFY(messageSender->numSentMessages == 0);
|
||||
QVERIFY(outgoingMessages.size() == 3);
|
||||
|
||||
f->setStatus(Status::Status::Online);
|
||||
|
||||
QVERIFY(messageSender->numSentActions == 1);
|
||||
QVERIFY(messageSender->numSentMessages == 2);
|
||||
QVERIFY(outgoingMessages.size() == 3);
|
||||
|
||||
auto lastReceipt = messageSender->receiptNum;
|
||||
for (auto i = firstReceipt; i < lastReceipt; ++i.get()) {
|
||||
friendMessageDispatcher->onReceiptReceived(i);
|
||||
}
|
||||
|
||||
QVERIFY(messageSender->numSentActions == 1);
|
||||
QVERIFY(messageSender->numSentMessages == 2);
|
||||
QVERIFY(outgoingMessages.size() == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Tests that messages that failed to send due to toxcore are resent later
|
||||
*/
|
||||
void TestFriendMessageDispatcher::testFailedMessage()
|
||||
{
|
||||
messageSender->canSend = false;
|
||||
|
||||
friendMessageDispatcher->sendMessage(false, "test");
|
||||
|
||||
QVERIFY(messageSender->numSentMessages == 0);
|
||||
|
||||
messageSender->canSend = true;
|
||||
f->setStatus(Status::Status::Offline);
|
||||
f->setStatus(Status::Status::Online);
|
||||
|
||||
QVERIFY(messageSender->numSentMessages == 1);
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(TestFriendMessageDispatcher)
|
||||
#include "friendmessagedispatcher_test.moc"
|
Loading…
x
Reference in New Issue
Block a user