File indexing completed on 2024-04-28 04:59:30

0001 // SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
0002 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0003 
0004 #include <QObject>
0005 #include <QSignalSpy>
0006 #include <QTest>
0007 
0008 #include <Quotient/connection.h>
0009 #include <Quotient/quotient_common.h>
0010 #include <Quotient/syncdata.h>
0011 
0012 #include "enums/delegatetype.h"
0013 #include "models/messageeventmodel.h"
0014 #include "neochatroom.h"
0015 
0016 #include "testutils.h"
0017 
0018 using namespace Quotient;
0019 
0020 class MessageEventModelTest : public QObject
0021 {
0022     Q_OBJECT
0023 
0024 private:
0025     Connection *connection = nullptr;
0026     MessageEventModel *model = nullptr;
0027 
0028 private Q_SLOTS:
0029     void initTestCase();
0030     void init();
0031 
0032     void switchEmptyRoom();
0033     void switchSyncedRoom();
0034     void simpleTimeline();
0035     void syncNewEvents();
0036     void pendingEvent();
0037     void disconnect();
0038     void idToRow();
0039 
0040     void cleanup();
0041 };
0042 
0043 void MessageEventModelTest::initTestCase()
0044 {
0045     connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
0046 }
0047 
0048 void MessageEventModelTest::init()
0049 {
0050     QCOMPARE(model, nullptr);
0051     model = new MessageEventModel;
0052 }
0053 
0054 // Make sure that basic empty rooms can be switched without crashing.
0055 void MessageEventModelTest::switchEmptyRoom()
0056 {
0057     auto firstRoom = new TestUtils::TestRoom(connection, QStringLiteral("#firstRoom:kde.org"));
0058     auto secondRoom = new TestUtils::TestRoom(connection, QStringLiteral("#secondRoom:kde.org"));
0059 
0060     QSignalSpy spy(model, SIGNAL(roomChanged()));
0061 
0062     QCOMPARE(model->room(), nullptr);
0063     model->setRoom(firstRoom);
0064     QCOMPARE(spy.count(), 1);
0065     QCOMPARE(model->room()->id(), QStringLiteral("#firstRoom:kde.org"));
0066     model->setRoom(secondRoom);
0067     QCOMPARE(spy.count(), 2);
0068     QCOMPARE(model->room()->id(), QStringLiteral("#secondRoom:kde.org"));
0069     model->setRoom(nullptr);
0070     QCOMPARE(spy.count(), 3);
0071     QCOMPARE(model->room(), nullptr);
0072 }
0073 
0074 // Make sure that rooms with some events can be switched without crashing
0075 void MessageEventModelTest::switchSyncedRoom()
0076 {
0077     auto firstRoom = new TestUtils::TestRoom(connection, QStringLiteral("#firstRoom:kde.org"), QLatin1String("test-messageventmodel-sync.json"));
0078     auto secondRoom = new TestUtils::TestRoom(connection, QStringLiteral("#secondRoom:kde.org"), QLatin1String("test-messageventmodel-sync.json"));
0079 
0080     QSignalSpy spy(model, SIGNAL(roomChanged()));
0081 
0082     QCOMPARE(model->room(), nullptr);
0083     model->setRoom(firstRoom);
0084     QCOMPARE(spy.count(), 1);
0085     QCOMPARE(model->room()->id(), QStringLiteral("#firstRoom:kde.org"));
0086     model->setRoom(secondRoom);
0087     QCOMPARE(spy.count(), 2);
0088     QCOMPARE(model->room()->id(), QStringLiteral("#secondRoom:kde.org"));
0089     model->setRoom(nullptr);
0090     QCOMPARE(spy.count(), 3);
0091     QCOMPARE(model->room(), nullptr);
0092 }
0093 
0094 void MessageEventModelTest::simpleTimeline()
0095 {
0096     auto room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), QLatin1String("test-messageventmodel-sync.json"));
0097 
0098     model->setRoom(room);
0099     QCOMPARE(model->rowCount(), 2);
0100 
0101     QCOMPARE(model->data(model->index(0), MessageEventModel::DelegateTypeRole), DelegateType::State);
0102     QCOMPARE(model->data(model->index(0)), QStringLiteral("changed their display name to Example Changed"));
0103 
0104     QCOMPARE(model->data(model->index(1)), QStringLiteral("<b>This is an example<br>text message</b>"));
0105     QCOMPARE(model->data(model->index(1), MessageEventModel::DelegateTypeRole), DelegateType::Message);
0106     QCOMPARE(model->data(model->index(1), MessageEventModel::PlainText), QStringLiteral("This is an example\ntext message"));
0107     QCOMPARE(model->data(model->index(1), MessageEventModel::EventIdRole), QStringLiteral("$153456789:example.org"));
0108 
0109     QTest::ignoreMessage(QtWarningMsg, "Index QModelIndex(-1,-1,0x0,QObject(0x0)) is not valid (expected valid)");
0110     QCOMPARE(model->data(model->index(-1)), QVariant());
0111     QTest::ignoreMessage(QtWarningMsg, "Index QModelIndex(-1,-1,0x0,QObject(0x0)) is not valid (expected valid)");
0112     QCOMPARE(model->data(model->index(model->rowCount())), QVariant());
0113 }
0114 
0115 // Sync some events into the MessageEventModel's current room and don't crash.
0116 void MessageEventModelTest::syncNewEvents()
0117 {
0118     auto room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"));
0119     QSignalSpy spy(room, SIGNAL(aboutToAddNewMessages(Quotient::RoomEventsRange)));
0120 
0121     model->setRoom(room);
0122     QCOMPARE(model->rowCount(), 0);
0123 
0124     room->syncNewEvents(QLatin1String("test-messageventmodel-sync.json"));
0125 
0126     QCOMPARE(model->rowCount(), 2);
0127     QCOMPARE(spy.count(), 1);
0128 }
0129 
0130 // Check the adding of pending events to the room doesn't cause any issues in the model.
0131 void MessageEventModelTest::pendingEvent()
0132 {
0133     QSignalSpy spyInsert(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)));
0134     QSignalSpy spyRemove(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)));
0135     QSignalSpy spyChanged(model, SIGNAL(dataChanged(const QModelIndex, const QModelIndex, const QList<int> &)));
0136 
0137     auto room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"));
0138     model->setRoom(room);
0139     QCOMPARE(model->rowCount(), 0);
0140 
0141     auto txnId = room->postPlainText("New plain message"_ls);
0142     QCOMPARE(model->rowCount(), 1);
0143     QCOMPARE(spyInsert.count(), 1);
0144 
0145     room->discardMessage(txnId);
0146     QCOMPARE(model->rowCount(), 0);
0147     QCOMPARE(spyRemove.count(), 1);
0148 
0149     txnId = room->postPlainText("New plain message"_ls);
0150     QCOMPARE(model->rowCount(), 1);
0151     QCOMPARE(spyInsert.count(), 2);
0152 
0153     // We need to manually set the transaction ID of the new message as it will be
0154     // different every time.
0155     QFile testSyncFile;
0156     testSyncFile.setFileName(QLatin1String(DATA_DIR) + u'/' + QLatin1String("test-pending-sync.json"));
0157     testSyncFile.open(QIODevice::ReadOnly);
0158     auto testSyncJson = QJsonDocument::fromJson(testSyncFile.readAll());
0159     auto root = testSyncJson.object();
0160     auto timeline = root["timeline"_ls].toObject();
0161     auto events = timeline["events"_ls].toArray();
0162     auto firstEvent = events[0].toObject();
0163     firstEvent.insert(QLatin1String("unsigned"), QJsonObject{{QLatin1String("transaction_id"), txnId}});
0164     events[0] = firstEvent;
0165     timeline.insert("events"_ls, events);
0166     root.insert("timeline"_ls, timeline);
0167     testSyncJson.setObject(root);
0168     SyncRoomData roomData(QStringLiteral("@bob:kde.org"), JoinState::Join, testSyncJson.object());
0169     room->update(std::move(roomData));
0170 
0171     QCOMPARE(model->rowCount(), 1);
0172     // The model will throw multiple data changed signals we need the one that refreshes
0173     // the IsPendingRole.
0174     QCOMPARE(spyChanged.count() > 0, true);
0175     auto isPendingChanged = false;
0176     for (auto signal : spyChanged) {
0177         auto roles = signal.at(2).toList();
0178         if (roles.contains(MessageEventModel::IsPendingRole)) {
0179             isPendingChanged = true;
0180         }
0181     }
0182     QCOMPARE(isPendingChanged, true);
0183 }
0184 
0185 // Make sure that the signals are disconnecting correctly when a room is switched.
0186 void MessageEventModelTest::disconnect()
0187 {
0188     auto room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"));
0189     model->setRoom(room);
0190 
0191     QSignalSpy spy(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)));
0192 
0193     model->setRoom(nullptr);
0194     room->syncNewEvents(QLatin1String("test-messageventmodel-sync.json"));
0195 
0196     QCOMPARE(spy.count(), 0);
0197 }
0198 
0199 void MessageEventModelTest::idToRow()
0200 {
0201     auto room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), QLatin1String("test-min-sync.json"));
0202     model->setRoom(room);
0203 
0204     QCOMPARE(model->eventIdToRow(QStringLiteral("$153456789:example.org")), 0);
0205 }
0206 
0207 void MessageEventModelTest::cleanup()
0208 {
0209     delete model;
0210     model = nullptr;
0211     QCOMPARE(model, nullptr);
0212 }
0213 
0214 QTEST_MAIN(MessageEventModelTest)
0215 #include "messageeventmodeltest.moc"