1
0
mirror of https://github.com/qTox/qTox.git synced 2024-03-22 14:00:36 +08:00
qTox/test/model/friendmessagedispatcher_test.cpp
Anthony Bilinski 9571434df9
feat(CI): Disallow unsafe implicit casts to QString
Covers char* without size, and QByteArray.

Catches the case of QByteArray to QString implicit conversion like was
fixed in 47ee51c61d, but still allows
construction or assignment from string literals in source. Gives most
of the type safety of QT_NO_CAST_FROM_ASCII without having to wrap
every literal string in QStringLiteral.

No functional issues found during change.
2022-04-05 12:10:05 -07:00

415 lines
13 KiB
C++

/*
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/icorefriendmessagesender.h"
#include "src/model/friend.h"
#include "src/model/friendmessagedispatcher.h"
#include "src/model/message.h"
#include <QObject>
#include <QtTest/QtTest>
#include <set>
#include <deque>
namespace {
constexpr uint64_t testMaxExtendedMessageSize = 10 * 1024 * 1024;
}
class MockCoreExtPacket : public ICoreExtPacket
{
public:
MockCoreExtPacket(uint64_t& numSentMessages_, uint64_t& currentReceiptId_)
: numSentMessages(numSentMessages_)
, currentReceiptId(currentReceiptId_)
{}
uint64_t addExtendedMessage(QString message_) override;
bool send() override;
uint64_t& numSentMessages;
uint64_t& currentReceiptId;
QDateTime senderTimestamp;
QString message;
};
uint64_t MockCoreExtPacket::addExtendedMessage(QString message_)
{
message = message_;
return currentReceiptId++;
}
bool MockCoreExtPacket::send()
{
numSentMessages++;
return true;
}
class MockCoreExtPacketAllocator : public ICoreExtPacketAllocator
{
public:
std::unique_ptr<ICoreExtPacket> getPacket(uint32_t friendId) override;
uint64_t numSentMessages;
uint64_t currentReceiptId;
};
std::unique_ptr<ICoreExtPacket> MockCoreExtPacketAllocator::getPacket(uint32_t friendId)
{
std::ignore = friendId;
return std::unique_ptr<MockCoreExtPacket>(new MockCoreExtPacket(numSentMessages, currentReceiptId));
}
class MockFriendMessageSender : public ICoreFriendMessageSender
{
public:
bool sendAction(uint32_t friendId, const QString& action, ReceiptNum& receipt) override;
bool sendMessage(uint32_t friendId, const QString& message, ReceiptNum& receipt) override;
bool canSend = true;
ReceiptNum receiptNum{0};
size_t numSentActions = 0;
size_t numSentMessages = 0;
};
bool MockFriendMessageSender::sendAction(uint32_t friendId, const QString& action, ReceiptNum& receipt)
{
std::ignore = friendId;
std::ignore = action;
if (canSend) {
numSentActions++;
receipt = receiptNum;
receiptNum.get() += 1;
}
return canSend;
}
bool MockFriendMessageSender::sendMessage(uint32_t friendId, const QString& message, ReceiptNum& receipt)
{
std::ignore = friendId;
std::ignore = message;
if (canSend) {
numSentMessages++;
receipt = receiptNum;
receiptNum.get() += 1;
}
return canSend;
}
class TestFriendMessageDispatcher : public QObject
{
Q_OBJECT
public:
TestFriendMessageDispatcher();
private slots:
void init();
void testSignals();
void testMessageSending();
void testOfflineMessages();
void testFailedMessage();
void testNegotiationFailure();
void testNegotiationSuccess();
void testOfflineExtensionMessages();
void testSentMessageExtensionSetReduced();
void testActionMessagesSplitWithExtensions();
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)
{
std::ignore = sender;
receivedMessages.push_back(std::move(message));
}
void onMessageBroken(DispatchedMessageId id, BrokenMessageReason reason)
{
std::ignore = reason;
brokenMessages.insert(id);
}
private:
// All unique_ptrs to make construction/init() easier to manage
std::unique_ptr<Friend> f;
std::unique_ptr<MockFriendMessageSender> messageSender;
std::unique_ptr<MockCoreExtPacketAllocator> coreExtPacketAllocator;
std::unique_ptr<MessageProcessor::SharedParams> sharedProcessorParams;
std::unique_ptr<MessageProcessor> messageProcessor;
std::unique_ptr<FriendMessageDispatcher> friendMessageDispatcher;
std::map<DispatchedMessageId, Message> outgoingMessages;
std::set<DispatchedMessageId> brokenMessages;
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);
f->onNegotiationComplete();
messageSender = std::unique_ptr<MockFriendMessageSender>(new MockFriendMessageSender());
coreExtPacketAllocator = std::unique_ptr<MockCoreExtPacketAllocator>(new MockCoreExtPacketAllocator());
sharedProcessorParams =
std::unique_ptr<MessageProcessor::SharedParams>(new MessageProcessor::SharedParams(tox_max_message_length(), testMaxExtendedMessageSize));
messageProcessor = std::unique_ptr<MessageProcessor>(new MessageProcessor(*sharedProcessorParams));
friendMessageDispatcher = std::unique_ptr<FriendMessageDispatcher>(
new FriendMessageDispatcher(*f, *messageProcessor, *messageSender, *coreExtPacketAllocator));
connect(friendMessageDispatcher.get(), &FriendMessageDispatcher::messageSent, this,
&TestFriendMessageDispatcher::onMessageSent);
connect(friendMessageDispatcher.get(), &FriendMessageDispatcher::messageComplete, this,
&TestFriendMessageDispatcher::onMessageComplete);
connect(friendMessageDispatcher.get(), &FriendMessageDispatcher::messageReceived, this,
&TestFriendMessageDispatcher::onMessageReceived);
connect(friendMessageDispatcher.get(), &FriendMessageDispatcher::messageBroken, this,
&TestFriendMessageDispatcher::onMessageBroken);
outgoingMessages = std::map<DispatchedMessageId, Message>();
receivedMessages = std::deque<Message>();
brokenMessages = std::set<DispatchedMessageId>();
}
/**
* @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);
f->onNegotiationComplete();
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);
f->onNegotiationComplete();
QVERIFY(messageSender->numSentMessages == 1);
}
void TestFriendMessageDispatcher::testNegotiationFailure()
{
f->setStatus(Status::Status::Offline);
f->setStatus(Status::Status::Online);
QVERIFY(f->getStatus() == Status::Status::Negotiating);
friendMessageDispatcher->sendMessage(false, "test");
QVERIFY(messageSender->numSentMessages == 0);
f->onNegotiationComplete();
QVERIFY(messageSender->numSentMessages == 1);
}
void TestFriendMessageDispatcher::testNegotiationSuccess()
{
f->setStatus(Status::Status::Offline);
f->setStatus(Status::Status::Online);
f->setExtendedMessageSupport(true);
f->onNegotiationComplete();
friendMessageDispatcher->sendMessage(false, "test");
QVERIFY(coreExtPacketAllocator->numSentMessages == 1);
friendMessageDispatcher->sendMessage(false, "test");
QVERIFY(coreExtPacketAllocator->numSentMessages == 2);
QVERIFY(messageSender->numSentMessages == 0);
}
void TestFriendMessageDispatcher::testOfflineExtensionMessages()
{
f->setStatus(Status::Status::Offline);
auto requiredExtensions = ExtensionSet();
requiredExtensions[ExtensionType::messages] = true;
friendMessageDispatcher->sendExtendedMessage("Test", requiredExtensions);
f->setStatus(Status::Status::Online);
f->setExtendedMessageSupport(true);
f->onNegotiationComplete();
// Ensure that when our friend came online with the desired extensions we
// were able to send them our message over the extended message path
QVERIFY(coreExtPacketAllocator->numSentMessages == 1);
f->setStatus(Status::Status::Offline);
friendMessageDispatcher->sendExtendedMessage("Test", requiredExtensions);
f->setStatus(Status::Status::Online);
f->setExtendedMessageSupport(false);
f->onNegotiationComplete();
// Here we want to make sure that when they do _not_ support extensions
// we discard the message instead of attempting to send it over either
// channel
QVERIFY(coreExtPacketAllocator->numSentMessages == 1);
QVERIFY(messageSender->numSentMessages == 0);
}
void TestFriendMessageDispatcher::testSentMessageExtensionSetReduced()
{
f->setStatus(Status::Status::Online);
f->setExtendedMessageSupport(true);
f->onNegotiationComplete();
friendMessageDispatcher->sendMessage(false, "Test");
f->setStatus(Status::Status::Offline);
f->setStatus(Status::Status::Online);
f->setExtendedMessageSupport(false);
f->onNegotiationComplete();
// Ensure that when we reduce our extension set we correctly emit the
// "messageBroken" signal
QVERIFY(brokenMessages.size() == 1);
}
void TestFriendMessageDispatcher::testActionMessagesSplitWithExtensions()
{
f->setStatus(Status::Status::Online);
f->setExtendedMessageSupport(true);
f->onNegotiationComplete();
auto reallyLongMessage = QString("a");
for (uint64_t i = 0; i < testMaxExtendedMessageSize + 50; ++i) {
reallyLongMessage += QString().number(i);
}
friendMessageDispatcher->sendMessage(true, reallyLongMessage);
QVERIFY(coreExtPacketAllocator->numSentMessages == 0);
QVERIFY(messageSender->numSentMessages == 0);
QVERIFY(messageSender->numSentActions > 1);
}
QTEST_GUILESS_MAIN(TestFriendMessageDispatcher)
#include "friendmessagedispatcher_test.moc"