1
0
mirror of https://github.com/qTox/qTox.git synced 2024-03-22 14:00:36 +08:00

refactor(offlinemsg): Decouple OfflineMsgEngine from other components

This commit is contained in:
Mick Sayson 2019-05-25 12:16:56 -07:00
parent d934cf372b
commit e5016337bb
9 changed files with 392 additions and 77 deletions

View File

@ -27,6 +27,7 @@ auto_test(net toxmedata)
auto_test(net bsu) auto_test(net bsu)
auto_test(persistence paths) auto_test(persistence paths)
auto_test(persistence dbschema) auto_test(persistence dbschema)
auto_test(persistence offlinemsgengine)
if (UNIX) if (UNIX)
auto_test(platform posixsignalnotifier) auto_test(platform posixsignalnotifier)

View File

@ -21,10 +21,12 @@
#ifndef CORE_HPP #ifndef CORE_HPP
#define CORE_HPP #define CORE_HPP
#include "groupid.h"
#include "icorefriendmessagesender.h"
#include "receiptnum.h"
#include "toxfile.h" #include "toxfile.h"
#include "toxid.h" #include "toxid.h"
#include "toxpk.h" #include "toxpk.h"
#include "groupid.h"
#include "src/util/strongtype.h" #include "src/util/strongtype.h"
#include "src/model/status.h" #include "src/model/status.h"
@ -47,10 +49,8 @@ class Profile;
class Core; class Core;
using ToxCorePtr = std::unique_ptr<Core>; using ToxCorePtr = std::unique_ptr<Core>;
using ReceiptNum = NamedType<uint32_t, struct ReceiptNumTag>;
Q_DECLARE_METATYPE(ReceiptNum);
class Core : public QObject class Core : public QObject, public ICoreFriendMessageSender
{ {
Q_OBJECT Q_OBJECT
public: public:
@ -114,11 +114,11 @@ public slots:
void setUsername(const QString& username); void setUsername(const QString& username);
void setStatusMessage(const QString& message); void setStatusMessage(const QString& message);
bool sendMessage(uint32_t friendId, const QString& message, ReceiptNum& receipt); bool sendMessage(uint32_t friendId, const QString& message, ReceiptNum& receipt) override;
void sendGroupMessage(int groupId, const QString& message); void sendGroupMessage(int groupId, const QString& message);
void sendGroupAction(int groupId, const QString& message); void sendGroupAction(int groupId, const QString& message);
void changeGroupTitle(int groupId, const QString& title); void changeGroupTitle(int groupId, const QString& title);
bool sendAction(uint32_t friendId, const QString& action, ReceiptNum& receipt); bool sendAction(uint32_t friendId, const QString& action, ReceiptNum& receipt) override;
void sendTyping(uint32_t friendId, bool typing); void sendTyping(uint32_t friendId, bool typing);
void setNospam(uint32_t nospam); void setNospam(uint32_t nospam);

View File

@ -0,0 +1,36 @@
/*
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 ICORE_FRIEND_MESSAGE_SENDER_H
#define ICORE_FRIEND_MESSAGE_SENDER_H
#include "receiptnum.h"
#include <QString>
#include <cstdint>
class ICoreFriendMessageSender
{
public:
virtual bool sendAction(uint32_t friendId, const QString& action, ReceiptNum& receipt) = 0;
virtual bool sendMessage(uint32_t friendId, const QString& message, ReceiptNum& receipt) = 0;
};
#endif /* ICORE_FRIEND_MESSAGE_SENDER_H */

31
src/core/receiptnum.h Normal file
View File

@ -0,0 +1,31 @@
/*
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 RECEIPT_NUM_H
#define RECEIPT_NUM_H
#include "src/util/strongtype.h"
#include <QMetaType>
#include <cstdint>
using ReceiptNum = NamedType<uint32_t, struct ReceiptNumTag>;
Q_DECLARE_METATYPE(ReceiptNum);
#endif /* RECEIPT_NUM_H */

33
src/model/message.h Normal file
View File

@ -0,0 +1,33 @@
/*
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 MESSAGE_H
#define MESSAGE_H
#include <QDateTime>
#include <QString>
struct Message
{
bool isAction;
QString content;
QDateTime timestamp;
};
#endif /*MESSAGE_H*/

View File

@ -28,11 +28,11 @@
#include <QCoreApplication> #include <QCoreApplication>
#include <chrono> #include <chrono>
OfflineMsgEngine::OfflineMsgEngine(Friend* frnd) OfflineMsgEngine::OfflineMsgEngine(Friend* frnd, ICoreFriendMessageSender* messageSender)
: mutex(QMutex::Recursive) : mutex(QMutex::Recursive)
, f(frnd) , f(frnd)
{ , messageSender(messageSender)
} {}
/** /**
* @brief Notification that the message is now delivered. * @brief Notification that the message is now delivered.
@ -59,18 +59,10 @@ void OfflineMsgEngine::onReceiptReceived(ReceiptNum receipt)
* @param[in] messageID database RowId of the message, used to eventually mark messages as received in history * @param[in] messageID database RowId of the message, used to eventually mark messages as received in history
* @param[in] msg chat message line in the chatlog, used to eventually set the message's receieved timestamp * @param[in] msg chat message line in the chatlog, used to eventually set the message's receieved timestamp
*/ */
void OfflineMsgEngine::addSavedMessage(RowId messageID, ChatMessage::Ptr chatMessage) void OfflineMsgEngine::addUnsentMessage(Message const& message, CompletionFn completionCallback)
{ {
QMutexLocker ml(&mutex); QMutexLocker ml(&mutex);
assert([&](){ unsentMessages.append(OfflineMessage{message, std::chrono::steady_clock::now(), completionCallback});
for (const auto& message : unsentSavedMessages) {
if (message.rowId == messageID) {
return false;
}
}
return true;
}());
unsentSavedMessages.append(Message{chatMessage, messageID, std::chrono::steady_clock::now()});
} }
/** /**
@ -83,11 +75,12 @@ void OfflineMsgEngine::addSavedMessage(RowId messageID, ChatMessage::Ptr chatMes
* @param[in] messageID database RowId of the message, used to eventually mark messages as received in history * @param[in] messageID database RowId of the message, used to eventually mark messages as received in history
* @param[in] msg chat message line in the chatlog, used to eventually set the message's receieved timestamp * @param[in] msg chat message line in the chatlog, used to eventually set the message's receieved timestamp
*/ */
void OfflineMsgEngine::addSentSavedMessage(ReceiptNum receipt, RowId messageID, ChatMessage::Ptr chatMessage) void OfflineMsgEngine::addSentMessage(ReceiptNum receipt, Message const& message,
CompletionFn completionCallback)
{ {
QMutexLocker ml(&mutex); QMutexLocker ml(&mutex);
assert(!sentSavedMessages.contains(receipt)); assert(!sentMessages.contains(receipt));
sentSavedMessages.insert(receipt, {chatMessage, messageID, std::chrono::steady_clock::now()}); sentMessages.insert(receipt, {message, std::chrono::steady_clock::now(), completionCallback});
checkForCompleteMessages(receipt); checkForCompleteMessages(receipt);
} }
@ -102,29 +95,31 @@ void OfflineMsgEngine::deliverOfflineMsgs()
return; return;
} }
if (sentSavedMessages.empty() && unsentSavedMessages.empty()) { if (sentMessages.empty() && unsentMessages.empty()) {
return; return;
} }
QVector<Message> messages = sentSavedMessages.values().toVector() + unsentSavedMessages; QVector<OfflineMessage> messages = sentMessages.values().toVector() + unsentMessages;
// order messages by authorship time to resend in same order as they were written // order messages by authorship time to resend in same order as they were written
qSort(messages.begin(), messages.end(), [](const Message& lhs, const Message& rhs){ return lhs.authorshipTime < rhs.authorshipTime; }); qSort(messages.begin(), messages.end(), [](const OfflineMessage& lhs, const OfflineMessage& rhs) {
return lhs.authorshipTime < rhs.authorshipTime;
});
removeAllMessages(); removeAllMessages();
for (const auto& message : messages) { for (const auto& message : messages) {
QString messageText = message.chatMessage->toString(); QString messageText = message.message.content;
ReceiptNum receipt; ReceiptNum receipt;
bool messageSent{false}; bool messageSent{false};
if (message.chatMessage->isAction()) { if (message.message.isAction) {
messageSent = Core::getInstance()->sendAction(f->getId(), messageText, receipt); messageSent = messageSender->sendAction(f->getId(), messageText, receipt);
} else { } else {
messageSent = Core::getInstance()->sendMessage(f->getId(), messageText, receipt); messageSent = messageSender->sendMessage(f->getId(), messageText, receipt);
} }
if (messageSent) { if (messageSent) {
addSentSavedMessage(receipt, message.rowId, message.chatMessage); addSentMessage(receipt, message.message, message.completionFn);
} else { } else {
qCritical() << "deliverOfflineMsgs failed to send message"; qCritical() << "deliverOfflineMsgs failed to send message";
addSavedMessage(message.rowId, message.chatMessage); addUnsentMessage(message.message, message.completionFn);
} }
} }
} }
@ -136,38 +131,23 @@ void OfflineMsgEngine::removeAllMessages()
{ {
QMutexLocker ml(&mutex); QMutexLocker ml(&mutex);
receivedReceipts.clear(); receivedReceipts.clear();
sentSavedMessages.clear(); sentMessages.clear();
unsentSavedMessages.clear(); unsentMessages.clear();
} }
void OfflineMsgEngine::completeMessage(QMap<ReceiptNum, Message>::iterator msgIt) void OfflineMsgEngine::completeMessage(QMap<ReceiptNum, OfflineMessage>::iterator msgIt)
{ {
Profile* const profile = Nexus::getProfile(); msgIt->completionFn();
if (profile->isHistoryEnabled()) { sentMessages.erase(msgIt);
profile->getHistory()->markAsSent(msgIt->rowId);
}
if (QThread::currentThread() == QCoreApplication::instance()->thread()) {
updateTimestamp(msgIt->chatMessage);
} else {
QMetaObject::invokeMethod(this, "updateTimestamp", Qt::BlockingQueuedConnection, Q_ARG(ChatMessage::Ptr, msgIt->chatMessage));
}
sentSavedMessages.erase(msgIt);
receivedReceipts.removeOne(msgIt.key()); receivedReceipts.removeOne(msgIt.key());
} }
void OfflineMsgEngine::checkForCompleteMessages(ReceiptNum receipt) void OfflineMsgEngine::checkForCompleteMessages(ReceiptNum receipt)
{ {
auto msgIt = sentSavedMessages.find(receipt); auto msgIt = sentMessages.find(receipt);
const bool receiptReceived = receivedReceipts.contains(receipt); const bool receiptReceived = receivedReceipts.contains(receipt);
if (!receiptReceived || msgIt == sentSavedMessages.end()) { if (!receiptReceived || msgIt == sentMessages.end()) {
return; return;
} }
assert(!unsentSavedMessages.contains(*msgIt));
completeMessage(msgIt); completeMessage(msgIt);
} }
void OfflineMsgEngine::updateTimestamp(ChatMessage::Ptr msg)
{
msg->markAsSent(QDateTime::currentDateTime());
}

View File

@ -22,6 +22,7 @@
#include "src/chatlog/chatmessage.h" #include "src/chatlog/chatmessage.h"
#include "src/core/core.h" #include "src/core/core.h"
#include "src/model/message.h"
#include "src/persistence/db/rawdatabase.h" #include "src/persistence/db/rawdatabase.h"
#include <QDateTime> #include <QDateTime>
#include <QMap> #include <QMap>
@ -31,14 +32,17 @@
#include <chrono> #include <chrono>
class Friend; class Friend;
class ICoreFriendMessageSender;
class OfflineMsgEngine : public QObject class OfflineMsgEngine : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit OfflineMsgEngine(Friend*); explicit OfflineMsgEngine(Friend* f, ICoreFriendMessageSender* messageSender);
void addSavedMessage(RowId messageID, ChatMessage::Ptr msg);
void addSentSavedMessage(ReceiptNum receipt, RowId messageID, ChatMessage::Ptr msg); using CompletionFn = std::function<void()>;
void addUnsentMessage(Message const& message, CompletionFn completionCallback);
void addSentMessage(ReceiptNum receipt, Message const& message, CompletionFn completionCallback);
void deliverOfflineMsgs(); void deliverOfflineMsgs();
public slots: public slots:
@ -46,28 +50,25 @@ public slots:
void onReceiptReceived(ReceiptNum receipt); void onReceiptReceived(ReceiptNum receipt);
private: private:
struct Message struct OfflineMessage
{ {
bool operator==(const Message& rhs) const { return rhs.rowId == rowId; } Message message;
ChatMessage::Ptr chatMessage;
RowId rowId;
std::chrono::time_point<std::chrono::steady_clock> authorshipTime; std::chrono::time_point<std::chrono::steady_clock> authorshipTime;
CompletionFn completionFn;
}; };
private slots: private slots:
void completeMessage(QMap<ReceiptNum, Message>::iterator msgIt); void completeMessage(QMap<ReceiptNum, OfflineMessage>::iterator msgIt);
private slots:
void updateTimestamp(ChatMessage::Ptr msg);
private: private:
void checkForCompleteMessages(ReceiptNum receipt); void checkForCompleteMessages(ReceiptNum receipt);
QMutex mutex; QMutex mutex;
const Friend* f; const Friend* f;
ICoreFriendMessageSender* messageSender;
QVector<ReceiptNum> receivedReceipts; QVector<ReceiptNum> receivedReceipts;
QMap<ReceiptNum, Message> sentSavedMessages; QMap<ReceiptNum, OfflineMessage> sentMessages;
QVector<Message> unsentSavedMessages; QVector<OfflineMessage> unsentMessages;
}; };
#endif // OFFLINEMSGENGINE_H #endif // OFFLINEMSGENGINE_H

View File

@ -102,6 +102,30 @@ namespace
return cD + res.sprintf("%02ds", seconds); return cD + res.sprintf("%02ds", seconds);
} }
void completeMessage(ChatMessage::Ptr ma, RowId rowId)
{
auto profile = Nexus::getProfile();
if (profile->isHistoryEnabled()) {
profile->getHistory()->markAsSent(rowId);
}
// force execution on the gui thread
QTimer::singleShot(0, QCoreApplication::instance(), [ma] {
ma->markAsSent(QDateTime::currentDateTime());
});
}
struct CompleteMessageFunctor
{
void operator()() const
{
completeMessage(ma, rowId);
}
ChatMessage::Ptr ma;
RowId rowId;
};
} // namespace } // namespace
ChatForm::ChatForm(Friend* chatFriend, History* history) ChatForm::ChatForm(Friend* chatFriend, History* history)
@ -122,7 +146,7 @@ ChatForm::ChatForm(Friend* chatFriend, History* history)
statusMessageLabel->setTextFormat(Qt::PlainText); statusMessageLabel->setTextFormat(Qt::PlainText);
statusMessageLabel->setContextMenuPolicy(Qt::CustomContextMenu); statusMessageLabel->setContextMenuPolicy(Qt::CustomContextMenu);
offlineEngine = new OfflineMsgEngine(f); offlineEngine = new OfflineMsgEngine(f, Core::getInstance());
typingTimer.setSingleShot(true); typingTimer.setSingleShot(true);
@ -919,20 +943,26 @@ void ChatForm::sendLoadedMessage(ChatMessage::Ptr chatMsg, MessageMetadata const
ReceiptNum receipt; ReceiptNum receipt;
bool messageSent{false}; bool messageSent{false};
QString stringMsg = chatMsg->toString();
if (f->isOnline()) { if (f->isOnline()) {
Core* core = Core::getInstance(); Core* core = Core::getInstance();
uint32_t friendId = f->getId(); uint32_t friendId = f->getId();
QString stringMsg = chatMsg->toString();
messageSent = metadata.isAction ? core->sendAction(friendId, stringMsg, receipt) messageSent = metadata.isAction ? core->sendAction(friendId, stringMsg, receipt)
: core->sendMessage(friendId, stringMsg, receipt); : core->sendMessage(friendId, stringMsg, receipt);
if (!messageSent) { if (!messageSent) {
qWarning() << "Failed to send loaded message, adding to offline messaging"; qWarning() << "Failed to send loaded message, adding to offline messaging";
} }
} }
auto onCompletion = CompleteMessageFunctor{};
onCompletion.ma = chatMsg;
onCompletion.rowId = metadata.id;
auto modelMsg = Message{metadata.isAction, stringMsg, QDateTime::currentDateTime()};
if (messageSent) { if (messageSent) {
getOfflineMsgEngine()->addSentSavedMessage(receipt, metadata.id, chatMsg); getOfflineMsgEngine()->addSentMessage(receipt, modelMsg, onCompletion);
} else { } else {
getOfflineMsgEngine()->addSavedMessage(metadata.id, chatMsg); getOfflineMsgEngine()->addUnsentMessage(modelMsg, onCompletion);
} }
} }
@ -1136,22 +1166,32 @@ void ChatForm::SendMessageStr(QString msg)
ChatMessage::Ptr ma = createSelfMessage(part, timestamp, isAction, false); ChatMessage::Ptr ma = createSelfMessage(part, timestamp, isAction, false);
Message modelMsg{isAction, part, timestamp};
if (history && Settings::getInstance().getEnableLogging()) { if (history && Settings::getInstance().getEnableLogging()) {
auto* offMsgEngine = getOfflineMsgEngine(); auto* offMsgEngine = getOfflineMsgEngine();
QString selfPk = Core::getInstance()->getSelfId().toString(); QString selfPk = Core::getInstance()->getSelfId().toString();
QString pk = f->getPublicKey().toString(); QString pk = f->getPublicKey().toString();
QString name = Core::getInstance()->getUsername(); QString name = Core::getInstance()->getUsername();
bool const isSent = false; // This forces history to add it to the offline messages table bool const isSent = false; // This forces history to add it to the offline messages table
// Use functor to avoid having to declare a lambda in a lambda
CompleteMessageFunctor onCompletion;
onCompletion.ma = ma;
history->addNewMessage(pk, historyPart, selfPk, timestamp, isSent, name, history->addNewMessage(pk, historyPart, selfPk, timestamp, isSent, name,
[messageSent, offMsgEngine, receipt, ma](RowId id) { [messageSent, offMsgEngine, receipt, modelMsg,
onCompletion](RowId id) mutable {
onCompletion.rowId = id;
if (messageSent) { if (messageSent) {
offMsgEngine->addSentSavedMessage(receipt, id, ma); offMsgEngine->addSentMessage(receipt, modelMsg,
onCompletion);
} else { } else {
offMsgEngine->addSavedMessage(id, ma); offMsgEngine->addUnsentMessage(modelMsg, onCompletion);
} }
}); });
} else { } else {
// TODO: Make faux-offline messaging work partially with the history disabled
ma->markAsSent(QDateTime::currentDateTime()); ma->markAsSent(QDateTime::currentDateTime());
} }

View File

@ -0,0 +1,193 @@
/*
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 "src/core/core.h"
#include "src/model/friend.h"
#include "src/persistence/offlinemsgengine.h"
#include <QtTest/QtTest>
struct MockFriendMessageSender : public QObject, public ICoreFriendMessageSender
{
Q_OBJECT
public:
MockFriendMessageSender(Friend* f)
: f(f){};
bool sendAction(uint32_t friendId, const QString& action, ReceiptNum& receipt) override
{
return false;
}
bool sendMessage(uint32_t friendId, const QString& message, ReceiptNum& receipt) override
{
if (f->isOnline()) {
receipt.get() = receiptNum++;
if (!dropReceipts) {
msgs.push_back(message);
emit receiptReceived(receipt);
}
numMessagesSent++;
} else {
numMessagesFailed++;
}
return f->isOnline();
}
signals:
void receiptReceived(ReceiptNum receipt);
public:
Friend* f;
bool dropReceipts = false;
size_t numMessagesSent = 0;
size_t numMessagesFailed = 0;
int receiptNum = 0;
std::vector<QString> msgs;
};
class TestOfflineMsgEngine : public QObject
{
Q_OBJECT
private slots:
void testReceiptResolution();
void testOfflineFriend();
void testSentUnsentCoordination();
void testCallback();
};
class OfflineMsgEngineFixture
{
public:
OfflineMsgEngineFixture()
: f(0, ToxPk(QByteArray(32, 0)))
, friendMessageSender(&f)
, offlineMsgEngine(&f, &friendMessageSender)
{
f.setStatus(Status::Status::Online);
QObject::connect(&friendMessageSender, &MockFriendMessageSender::receiptReceived,
&offlineMsgEngine, &OfflineMsgEngine::onReceiptReceived);
}
Friend f;
MockFriendMessageSender friendMessageSender;
OfflineMsgEngine offlineMsgEngine;
};
void completionFn() {}
void TestOfflineMsgEngine::testReceiptResolution()
{
OfflineMsgEngineFixture fixture;
Message msg{false, QString(), QDateTime()};
ReceiptNum receipt;
fixture.friendMessageSender.sendMessage(0, msg.content, receipt);
fixture.offlineMsgEngine.addSentMessage(receipt, msg, completionFn);
// We should have no offline messages to deliver if we resolved our receipt
// correctly
fixture.offlineMsgEngine.deliverOfflineMsgs();
fixture.offlineMsgEngine.deliverOfflineMsgs();
fixture.offlineMsgEngine.deliverOfflineMsgs();
QVERIFY(fixture.friendMessageSender.numMessagesSent == 1);
// If we drop receipts we should keep trying to send messages every time we
// "deliverOfflineMsgs"
fixture.friendMessageSender.dropReceipts = true;
fixture.friendMessageSender.sendMessage(0, msg.content, receipt);
fixture.offlineMsgEngine.addSentMessage(receipt, msg, completionFn);
fixture.offlineMsgEngine.deliverOfflineMsgs();
fixture.offlineMsgEngine.deliverOfflineMsgs();
fixture.offlineMsgEngine.deliverOfflineMsgs();
QVERIFY(fixture.friendMessageSender.numMessagesSent == 5);
// And once we stop dropping and try one more time we should run out of
// messages to send again
fixture.friendMessageSender.dropReceipts = false;
fixture.offlineMsgEngine.deliverOfflineMsgs();
fixture.offlineMsgEngine.deliverOfflineMsgs();
fixture.offlineMsgEngine.deliverOfflineMsgs();
QVERIFY(fixture.friendMessageSender.numMessagesSent == 6);
}
void TestOfflineMsgEngine::testOfflineFriend()
{
OfflineMsgEngineFixture fixture;
Message msg{false, QString(), QDateTime()};
fixture.f.setStatus(Status::Status::Offline);
fixture.offlineMsgEngine.addUnsentMessage(msg, completionFn);
fixture.offlineMsgEngine.addUnsentMessage(msg, completionFn);
fixture.offlineMsgEngine.addUnsentMessage(msg, completionFn);
fixture.offlineMsgEngine.addUnsentMessage(msg, completionFn);
fixture.offlineMsgEngine.addUnsentMessage(msg, completionFn);
fixture.f.setStatus(Status::Status::Online);
fixture.offlineMsgEngine.deliverOfflineMsgs();
QVERIFY(fixture.friendMessageSender.numMessagesFailed == 0);
QVERIFY(fixture.friendMessageSender.numMessagesSent == 5);
}
void TestOfflineMsgEngine::testSentUnsentCoordination()
{
OfflineMsgEngineFixture fixture;
Message msg{false, QString("a"), QDateTime()};
ReceiptNum receipt;
fixture.offlineMsgEngine.addUnsentMessage(msg, completionFn);
msg.content = "b";
fixture.friendMessageSender.dropReceipts = true;
fixture.friendMessageSender.sendMessage(0, msg.content, receipt);
fixture.friendMessageSender.dropReceipts = false;
fixture.offlineMsgEngine.addSentMessage(receipt, msg, completionFn);
msg.content = "c";
fixture.offlineMsgEngine.addUnsentMessage(msg, completionFn);
fixture.offlineMsgEngine.deliverOfflineMsgs();
auto expectedResponseOrder = std::vector<QString>{"a", "b", "c"};
QVERIFY(fixture.friendMessageSender.msgs == expectedResponseOrder);
}
void TestOfflineMsgEngine::testCallback()
{
OfflineMsgEngineFixture fixture;
size_t numCallbacks = 0;
auto callback = [&numCallbacks] { numCallbacks++; };
Message msg{false, QString(), QDateTime()};
ReceiptNum receipt;
fixture.friendMessageSender.sendMessage(0, msg.content, receipt);
fixture.offlineMsgEngine.addSentMessage(receipt, msg, callback);
fixture.offlineMsgEngine.addUnsentMessage(msg, callback);
fixture.offlineMsgEngine.deliverOfflineMsgs();
QVERIFY(numCallbacks == 2);
}
QTEST_GUILESS_MAIN(TestOfflineMsgEngine)
#include "offlinemsgengine_test.moc"