File indexing completed on 2024-11-10 04:40:15

0001 /*
0002     SPDX-FileCopyrightText: 2016 David Faure <faure@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "statisticsproxymodel.h"
0008 #include "collection.h"
0009 #include "collectionstatistics.h"
0010 #include "entitytreemodel.h"
0011 #include "test_model_helpers.h"
0012 
0013 #include <QStandardItemModel>
0014 #include <QTest>
0015 
0016 using namespace TestModelHelpers;
0017 
0018 using Akonadi::StatisticsProxyModel;
0019 
0020 #ifndef Q_OS_WIN
0021 void initLocale()
0022 {
0023     setenv("LC_ALL", "en_US.utf-8", 1);
0024 }
0025 Q_CONSTRUCTOR_FUNCTION(initLocale)
0026 #endif
0027 
0028 class StatisticsProxyModelTest : public QObject
0029 {
0030     Q_OBJECT
0031 
0032 private Q_SLOTS:
0033     void initTestCase();
0034 
0035     void init();
0036     void shouldDoNothingIfNoExtraColumns();
0037     void shouldShowExtraColumns();
0038     void shouldShowToolTip();
0039     void shouldHandleDataChanged();
0040     void shouldHandleDataChangedInExtraColumn();
0041 
0042 private:
0043     QStandardItemModel m_model;
0044 };
0045 
0046 // Helper functions
0047 
0048 static QString indexToText(const QModelIndex &index)
0049 {
0050     if (!index.isValid()) {
0051         return QStringLiteral("invalid");
0052     }
0053     return QString::number(index.row()) + QLatin1Char(',') + QString::number(index.column()) + QLatin1Char(',')
0054         + QString::number(reinterpret_cast<qulonglong>(index.internalPointer()), 16) + QLatin1StringView(" in ")
0055         + QString::number(reinterpret_cast<qulonglong>(index.model()), 16);
0056 }
0057 
0058 static QString indexRowCol(const QModelIndex &index)
0059 {
0060     if (!index.isValid()) {
0061         return QStringLiteral("invalid");
0062     }
0063     return QString::number(index.row()) + QLatin1Char(',') + QString::number(index.column());
0064 }
0065 
0066 static Akonadi::CollectionStatistics makeStats(qint64 unread, qint64 count, qint64 size)
0067 {
0068     Akonadi::CollectionStatistics stats;
0069     stats.setUnreadCount(unread);
0070     stats.setCount(count);
0071     stats.setSize(size);
0072     return stats;
0073 }
0074 
0075 void StatisticsProxyModelTest::initTestCase()
0076 {
0077 }
0078 
0079 void StatisticsProxyModelTest::init()
0080 {
0081     // Prepare the source model to use later on
0082     m_model.clear();
0083     m_model.appendRow(makeStandardItems(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C") << QStringLiteral("D")));
0084     m_model.item(0, 0)->appendRow(makeStandardItems(QStringList() << QStringLiteral("m") << QStringLiteral("n") << QStringLiteral("o") << QStringLiteral("p")));
0085     m_model.item(0, 0)->appendRow(makeStandardItems(QStringList() << QStringLiteral("q") << QStringLiteral("r") << QStringLiteral("s") << QStringLiteral("t")));
0086     m_model.appendRow(makeStandardItems(QStringList() << QStringLiteral("E") << QStringLiteral("F") << QStringLiteral("G") << QStringLiteral("H")));
0087     m_model.item(1, 0)->appendRow(makeStandardItems(QStringList() << QStringLiteral("x") << QStringLiteral("y") << QStringLiteral("z") << QStringLiteral(".")));
0088     m_model.setHorizontalHeaderLabels(QStringList() << QStringLiteral("H1") << QStringLiteral("H2") << QStringLiteral("H3") << QStringLiteral("H4"));
0089 
0090     // Set Collection c1 for row A
0091     Akonadi::Collection c1(1);
0092     c1.setName(QStringLiteral("c1"));
0093     c1.setStatistics(makeStats(2, 6, 9)); // unread, count, size in bytes
0094     m_model.item(0, 0)->setData(QVariant::fromValue(c1), Akonadi::EntityTreeModel::CollectionRole);
0095 
0096     // Set Collection c2 for first child (row m)
0097     Akonadi::Collection c2(2);
0098     c2.setName(QStringLiteral("c2"));
0099     c2.setStatistics(makeStats(1, 3, 4)); // unread, count, size in bytes
0100     m_model.item(0, 0)->child(0)->setData(QVariant::fromValue(c2), Akonadi::EntityTreeModel::CollectionRole);
0101 
0102     // Set Collection c2 for first child (row m)
0103     Akonadi::Collection c3(3);
0104     c3.setName(QStringLiteral("c3"));
0105     c3.setStatistics(makeStats(0, 1, 1)); // unread, count, size in bytes
0106     m_model.item(0, 0)->child(1)->setData(QVariant::fromValue(c3), Akonadi::EntityTreeModel::CollectionRole);
0107 
0108     QCOMPARE(extractRowTexts(&m_model, 0), QStringLiteral("ABCD"));
0109     QCOMPARE(extractRowTexts(&m_model, 0, m_model.index(0, 0)), QStringLiteral("mnop"));
0110     QCOMPARE(extractRowTexts(&m_model, 1, m_model.index(0, 0)), QStringLiteral("qrst"));
0111     QCOMPARE(extractRowTexts(&m_model, 1), QStringLiteral("EFGH"));
0112     QCOMPARE(extractRowTexts(&m_model, 0, m_model.index(1, 0)), QStringLiteral("xyz."));
0113     QCOMPARE(extractHorizontalHeaderTexts(&m_model), QStringLiteral("H1H2H3H4"));
0114 }
0115 
0116 void StatisticsProxyModelTest::shouldDoNothingIfNoExtraColumns()
0117 {
0118     // Given a statistics proxy with no extra columns
0119     StatisticsProxyModel pm;
0120     pm.setExtraColumnsEnabled(false);
0121 
0122     // When setting it to a source model
0123     pm.setSourceModel(&m_model);
0124 
0125     // Then the proxy should show the same as the model
0126     QCOMPARE(pm.rowCount(), m_model.rowCount());
0127     QCOMPARE(pm.columnCount(), m_model.columnCount());
0128 
0129     QCOMPARE(pm.rowCount(pm.index(0, 0)), 2);
0130     QCOMPARE(pm.index(0, 0).parent(), QModelIndex());
0131 
0132     // (verify that the mapFromSource(mapToSource(x)) == x roundtrip works)
0133     for (int row = 0; row < pm.rowCount(); ++row) {
0134         for (int col = 0; col < pm.columnCount(); ++col) {
0135             QCOMPARE(pm.mapFromSource(pm.mapToSource(pm.index(row, col))), pm.index(row, col));
0136         }
0137     }
0138 
0139     QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABCD"));
0140     QCOMPARE(extractRowTexts(&pm, 0, pm.index(0, 0)), QStringLiteral("mnop"));
0141     QCOMPARE(extractRowTexts(&pm, 1, pm.index(0, 0)), QStringLiteral("qrst"));
0142     QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("EFGH"));
0143     QCOMPARE(extractRowTexts(&pm, 0, pm.index(1, 0)), QStringLiteral("xyz."));
0144     QCOMPARE(extractHorizontalHeaderTexts(&pm), QStringLiteral("H1H2H3H4"));
0145 }
0146 
0147 void StatisticsProxyModelTest::shouldShowExtraColumns()
0148 {
0149     // Given a extra-columns proxy with three extra columns
0150     StatisticsProxyModel pm;
0151 
0152     // When setting it to a source model
0153     pm.setSourceModel(&m_model);
0154 
0155     // Then the proxy should show the extra column
0156     QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABCD269 B"));
0157     QCOMPARE(extractRowTexts(&pm, 0, pm.index(0, 0)), QStringLiteral("mnop134 B"));
0158     QCOMPARE(extractRowTexts(&pm, 1, pm.index(0, 0)), QStringLiteral("qrst 11 B"));
0159     QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("EFGH   "));
0160     QCOMPARE(extractRowTexts(&pm, 0, pm.index(1, 0)), QStringLiteral("xyz.   "));
0161     QCOMPARE(extractHorizontalHeaderTexts(&pm), QStringLiteral("H1H2H3H4UnreadTotalSize"));
0162 
0163     // Verify tree structure of proxy
0164     const QModelIndex secondParent = pm.index(1, 0);
0165     QVERIFY(!secondParent.parent().isValid());
0166     QCOMPARE(indexToText(pm.index(0, 0, secondParent).parent()), indexToText(secondParent));
0167     QCOMPARE(indexToText(pm.index(0, 3, secondParent).parent()), indexToText(secondParent));
0168     QVERIFY(indexToText(pm.index(0, 4)).startsWith(QLatin1StringView("0,4,")));
0169     QCOMPARE(indexToText(pm.index(0, 4, secondParent).parent()), indexToText(secondParent));
0170     QVERIFY(indexToText(pm.index(0, 5)).startsWith(QLatin1StringView("0,5,")));
0171     QCOMPARE(indexToText(pm.index(0, 5, secondParent).parent()), indexToText(secondParent));
0172 
0173     QCOMPARE(pm.index(0, 0).sibling(0, 4).column(), 4);
0174     QCOMPARE(pm.index(0, 4).sibling(0, 1).column(), 1);
0175 
0176     QVERIFY(!pm.canFetchMore(QModelIndex()));
0177 }
0178 
0179 void StatisticsProxyModelTest::shouldShowToolTip()
0180 {
0181     // Given a extra-columns proxy with three extra columns
0182     StatisticsProxyModel pm;
0183     pm.setSourceModel(&m_model);
0184 
0185     // When enabling tooltips and getting the tooltip for the first folder
0186     pm.setToolTipEnabled(true);
0187     QString toolTip = pm.index(0, 0).data(Qt::ToolTipRole).toString();
0188 
0189     // Then the tooltip should contain the expected information
0190     toolTip.remove(QStringLiteral("<strong>"));
0191     toolTip.remove(QStringLiteral("</strong>"));
0192     QVERIFY2(toolTip.contains(QLatin1StringView("Total Messages: 6")), qPrintable(toolTip));
0193     QVERIFY2(toolTip.contains(QLatin1StringView("Unread Messages: 2")), qPrintable(toolTip));
0194     QVERIFY2(toolTip.contains(QLatin1StringView("Storage Size: 9 B")), qPrintable(toolTip));
0195     QVERIFY2(toolTip.contains(QLatin1StringView("Subfolder Storage Size: 5 B")), qPrintable(toolTip));
0196 }
0197 
0198 void StatisticsProxyModelTest::shouldHandleDataChanged()
0199 {
0200     // Given a extra-columns proxy with three extra columns
0201     StatisticsProxyModel pm;
0202     pm.setSourceModel(&m_model);
0203     QSignalSpy dataChangedSpy(&pm, &QAbstractItemModel::dataChanged);
0204 
0205     // When ETM says the collection changed
0206     m_model.item(0, 0)->setData(QStringLiteral("a"), Qt::EditRole);
0207 
0208     // Then the change should be notified to the proxy -- including the extra columns
0209     QCOMPARE(dataChangedSpy.count(), 1);
0210     QCOMPARE(indexRowCol(dataChangedSpy.at(0).at(0).toModelIndex()), QStringLiteral("0,0"));
0211     QCOMPARE(indexRowCol(dataChangedSpy.at(0).at(1).toModelIndex()), QStringLiteral("0,6"));
0212     QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("aBCD269 B"));
0213 }
0214 
0215 void StatisticsProxyModelTest::shouldHandleDataChangedInExtraColumn()
0216 {
0217     // Given a extra-columns proxy with three extra columns
0218     StatisticsProxyModel pm;
0219     pm.setSourceModel(&m_model);
0220     QSignalSpy dataChangedSpy(&pm, &QAbstractItemModel::dataChanged);
0221 
0222     // When the proxy wants to signal a change in an extra column
0223     Akonadi::Collection c1(1);
0224     c1.setName(QStringLiteral("c1"));
0225     c1.setStatistics(makeStats(3, 5, 8)); // changed: unread, count, size in bytes
0226 
0227     // trick QStandardItemModel into noticing something changed here
0228     m_model.item(0, 0)->setData(QVariant::fromValue(Akonadi::Collection()), Akonadi::EntityTreeModel::CollectionRole);
0229     dataChangedSpy.clear();
0230     m_model.item(0, 0)->setData(QVariant::fromValue(c1), Akonadi::EntityTreeModel::CollectionRole);
0231 
0232     // Then the change should be available and notified
0233     QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABCD358 B"));
0234     QCOMPARE(dataChangedSpy.count(), 1);
0235     QCOMPARE(indexRowCol(dataChangedSpy.at(0).at(0).toModelIndex()), QStringLiteral("0,0"));
0236     QCOMPARE(indexRowCol(dataChangedSpy.at(0).at(1).toModelIndex()), QStringLiteral("0,6"));
0237 }
0238 
0239 #include "statisticsproxymodeltest.moc"
0240 
0241 QTEST_MAIN(StatisticsProxyModelTest)