File indexing completed on 2024-05-12 17:09:58

0001 /*
0002     SPDX-FileCopyrightText: 2017 David Edmundson <davidedmundson@kde.org>
0003     SPDX-FileCopyrightText: 2020 Kai Uwe Broulik <kde@broulik.de>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include <QDebug>
0009 #include <QObject>
0010 #include <QtTest>
0011 
0012 #include "notification.h"
0013 #include "notificationsmodel.h"
0014 #include "server.h"
0015 
0016 namespace NotificationManager
0017 {
0018 class NotificationTest : public QObject
0019 {
0020     Q_OBJECT
0021 public:
0022     NotificationTest()
0023     {
0024     }
0025 private Q_SLOTS:
0026     void parse_data();
0027     void parse();
0028 
0029     void compressNotificationRemoval();
0030 };
0031 
0032 void NotificationTest::parse_data()
0033 {
0034     QTest::addColumn<QString>("messageIn");
0035     QTest::addColumn<QString>("expectedOut");
0036 
0037     // clang-format off
0038     QTest::newRow("basic no HTML") << "I am a notification" << "I am a notification";
0039     QTest::newRow("whitespace") << "      I am a   notification  " << "I am a notification";
0040 
0041     QTest::newRow("basic html") << "I am <b>the</b> notification" << "I am <b>the</b> notification";
0042     QTest::newRow("nested html") << "I am <i><b>the</b></i> notification" << "I am <i><b>the</b></i> notification";
0043 
0044     QTest::newRow("no extra tags") << "I am <blink>the</blink> notification" << "I am the notification";
0045     QTest::newRow("no extra attrs") << "I am <b style=\"font-weight:20\">the</b> notification" << "I am <b>the</b> notification";
0046 
0047     QTest::newRow("newlines") << "I am\nthe\nnotification" << "I am<br/>the<br/>notification";
0048     QTest::newRow("multinewlines") << "I am\n\nthe\n\n\nnotification" << "I am<br/>the<br/>notification";
0049 
0050     QTest::newRow("amp") << "me&you" << "me&amp;you";
0051     QTest::newRow("double escape") << "foo &amp; &lt;bar&gt;" << "foo &amp; &lt;bar&gt;";
0052 
0053     QTest::newRow("quotes") << "&apos;foo&apos;" << "'foo'";//as label can't handle this normally valid entity
0054 
0055     QTest::newRow("image normal") << "This is <img src=\"file:://foo/boo.png\" alt=\"cheese\"/> and more text" << "This is <img src=\"file:://foo/boo.png\" alt=\"cheese\"/> and more text";
0056 
0057     //this input is technically wrong, so the output is also wrong, but QTextHtmlParser does the "right" thing
0058     QTest::newRow("image normal no close") << "This is <img src=\"file:://foo/boo.png\" alt=\"cheese\"> and more text" << "This is <img src=\"file:://foo/boo.png\" alt=\"cheese\"> and more text</img>";
0059 
0060     QTest::newRow("image remote URL") << "This is <img src=\"http://foo.com/boo.png\" alt=\"cheese\" /> and more text" << "This is <img alt=\"cheese\"/> and more text";
0061 
0062     //more bad formatted options. To some extent actual output doesn't matter. Garbage in, garbage out.
0063     //the important thing is that it doesn't contain anything that could be parsed as the remote URL
0064     QTest::newRow("image remote URL no close") << "This is <img src=\"http://foo.com/boo.png>\" alt=\"cheese\">  and more text" << "This is <img alt=\"cheese\"> and more text</img>";
0065     QTest::newRow("image remote URL double open") << "This is <<img src=\"http://foo.com/boo.png>\"  and more text" << "This is ";
0066     QTest::newRow("image remote URL no entity close") << "This is <img src=\"http://foo.com/boo.png\"  and more text" << "This is ";
0067     QTest::newRow("image remote URL space in element name") << "This is < img src=\"http://foo.com/boo.png\" alt=\"cheese\" /> and more text" << "This is ";
0068 
0069     QTest::newRow("link") << "This is a link <a href=\"http://foo.com/boo\"/> and more text" << "This is a link <a href=\"http://foo.com/boo\"/> and more text";
0070     // clang-format on
0071 }
0072 
0073 void NotificationTest::parse()
0074 {
0075     QFETCH(QString, messageIn);
0076     QFETCH(QString, expectedOut);
0077 
0078     NotificationManager::Notification notification;
0079     notification.setBody(messageIn);
0080 
0081     expectedOut = "<?xml version=\"1.0\"?><html>" + expectedOut + "</html>\n";
0082 
0083     QCOMPARE(notification.body(), expectedOut);
0084 }
0085 
0086 void NotificationTest::compressNotificationRemoval()
0087 {
0088     const int notificationCount = 10;
0089     const int gapId = 4;
0090 
0091     auto model = NotificationsModel::createNotificationsModel();
0092 
0093     QSignalSpy rowsRemovedSpy(model.data(), &QAbstractItemModel::rowsRemoved);
0094     QVERIFY(rowsRemovedSpy.isValid());
0095 
0096     for (uint i = 1; i <= notificationCount; ++i) {
0097         Notification notification{i};
0098         notification.setSummary(QStringLiteral("Notification %1").arg(i));
0099         model->onNotificationAdded(notification);
0100     }
0101 
0102     QCOMPARE(model->rowCount(), notificationCount);
0103 
0104     for (uint i = 1; i <= notificationCount; ++i) {
0105         // Leave a gap inbetween
0106         if (i != gapId) {
0107             model->onNotificationRemoved(i, Server::CloseReason::Revoked);
0108         }
0109     }
0110 
0111     // We should have two ranges that we ended up removing
0112     QTRY_COMPARE(rowsRemovedSpy.count(), 2);
0113 
0114     // The fact that it emits row removal in reverse order is an implementation detail
0115     // We only really care that the number of rows emitted matches our expectation
0116     int removedCount = 0;
0117     for (const auto &removedEmission : rowsRemovedSpy) {
0118         const int from = removedEmission.at(1).toInt();
0119         const int to = removedEmission.at(2).toInt();
0120         removedCount += (to - from) + 1;
0121     }
0122 
0123     QCOMPARE(removedCount, notificationCount - 1);
0124     QCOMPARE(model->rowCount(), 1);
0125 
0126     rowsRemovedSpy.clear();
0127 
0128     // Removing a random non-existing notification should noop
0129     model->onNotificationRemoved(3, Server::CloseReason::Revoked);
0130     QTRY_COMPARE(rowsRemovedSpy.count(), 0);
0131     QCOMPARE(model->rowCount(), 1);
0132     rowsRemovedSpy.clear();
0133 
0134     // Now remove the last one
0135     model->onNotificationRemoved(gapId, Server::CloseReason::Revoked);
0136     QTRY_COMPARE(rowsRemovedSpy.count(), 1);
0137     QCOMPARE(rowsRemovedSpy.at(0).at(1), 0); // from
0138     QCOMPARE(rowsRemovedSpy.at(0).at(2), 0); // to
0139     QCOMPARE(model->rowCount(), 0);
0140 }
0141 
0142 } // namespace NotificationManager
0143 
0144 QTEST_GUILESS_MAIN(NotificationManager::NotificationTest)
0145 
0146 #include "notifications_test.moc"