File indexing completed on 2025-01-26 05:06:20

0001 /*
0002     SPDX-FileCopyrightText: 2017 Klarälvdalens Datakonsult AB a KDAB Group company <info@kdab.com>
0003     SPDX-FileCopyrightText: Andras Mantia <andras.mantia@kdab.com>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "positionertest.h"
0009 
0010 #include <QSignalSpy>
0011 #include <QTemporaryDir>
0012 #include <QTest>
0013 
0014 #include "foldermodel.h"
0015 #include "positioner.h"
0016 #include "screenmapper.h"
0017 
0018 QTEST_MAIN(PositionerTest)
0019 
0020 static const QLatin1String desktop(QLatin1String("Desktop"));
0021 
0022 void PositionerTest::initTestCase()
0023 {
0024     m_currentActivity = QStringLiteral("00000000-0000-0000-0000-000000000000");
0025     m_folderDir = new QTemporaryDir();
0026 
0027     QDir dir(m_folderDir->path());
0028     dir.mkdir(desktop);
0029     dir.cd(desktop);
0030     dir.mkdir(QStringLiteral("firstDir"));
0031     QFile f;
0032     for (int i = 1; i < 10; i++) {
0033         f.setFileName(QStringLiteral("%1/file%2.txt").arg(dir.path(), QString::number(i)));
0034         f.open(QFile::WriteOnly);
0035         f.close();
0036     }
0037 }
0038 
0039 void PositionerTest::cleanupTestCase()
0040 {
0041     delete m_folderDir;
0042 }
0043 
0044 void PositionerTest::init()
0045 {
0046     m_folderModel = new FolderModel(this);
0047     m_folderModel->classBegin();
0048     m_folderModel->setScreen(0);
0049     m_folderModel->setUsedByContainment(true);
0050     m_folderModel->componentComplete();
0051     m_positioner = new Positioner(this);
0052     m_positioner->setEnabled(true);
0053     m_positioner->setFolderModel(m_folderModel);
0054     m_positioner->setPerStripe(3);
0055 
0056     m_folderModel->setUrl(m_folderDir->path() + QDir::separator() + desktop);
0057     QSignalSpy s(m_folderModel, &FolderModel::listingCompleted);
0058     s.wait(1000);
0059 }
0060 
0061 void PositionerTest::cleanup()
0062 {
0063     delete m_folderModel;
0064     m_folderModel = nullptr;
0065     delete m_positioner;
0066     m_positioner = nullptr;
0067 }
0068 
0069 void PositionerTest::tst_positions_data()
0070 {
0071     QTest::addColumn<int>("perStripe");
0072     QTest::newRow("3 per column") << 3;
0073     QTest::newRow("5 per column") << 5;
0074 }
0075 
0076 void PositionerTest::tst_positions()
0077 {
0078     QFETCH(int, perStripe);
0079     m_positioner->setPerStripe(perStripe);
0080     checkPositions(perStripe);
0081 }
0082 
0083 void PositionerTest::tst_map()
0084 {
0085     // by default the mapping is 1-1
0086     for (int i = 0; i < m_positioner->rowCount(); i++) {
0087         QCOMPARE(m_positioner->map(i), i);
0088     }
0089 }
0090 
0091 void PositionerTest::tst_move_data()
0092 {
0093     QTest::addColumn<QVariantList>("moves");
0094     QTest::addColumn<QList<int>>("result");
0095     QTest::newRow("First to last") << QVariantList({0, 10}) << QList<int>({-1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0});
0096     QTest::newRow("First to after last") << QVariantList({0, 11}) << QList<int>({-1, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, 0});
0097     QTest::newRow("Switch 2nd with 3rd ") << QVariantList({1, 2, 2, 1}) << QList<int>({0, 2, 1, 3, 4, 5, 6, 7, 8, 9});
0098     QTest::newRow("Switch 2nd with 2nd ") << QVariantList({1, 1, 1, 1}) << QList<int>({0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
0099     QTest::newRow("2nd to last") << QVariantList({2, 10}) << QList<int>({0, 1, -1, 3, 4, 5, 6, 7, 8, 9, 2});
0100 }
0101 
0102 void PositionerTest::tst_move()
0103 {
0104     QFETCH(QVariantList, moves);
0105     QFETCH(QList<int>, result);
0106 
0107     ensureFolderModelReady();
0108     m_positioner->move(moves);
0109     for (int i = 0; i < m_positioner->rowCount(); i++) {
0110         QCOMPARE(m_positioner->map(i), result[i]);
0111     }
0112 }
0113 
0114 void PositionerTest::tst_nearestitem_data()
0115 {
0116     QTest::addColumn<int>("index");
0117     QTest::addColumn<QList<int>>("result");
0118     QTest::newRow("Around first") << 0 << QList<int>{-1, -1, 3, -1, 1};
0119     QTest::newRow("Around second") << 1 << QList<int>{-1, -1, 4, 0, 2};
0120     QTest::newRow("Around 5th") << 4 << QList<int>{-1, 1, 7, 3, 5};
0121     QTest::newRow("Around last") << 9 << QList<int>{-1, 6, -1, -1, 7};
0122     QTest::newRow("Around invalid") << 11 << QList<int>{-1, -1, -1, -1, -1};
0123 }
0124 
0125 void PositionerTest::tst_nearestitem()
0126 {
0127     QFETCH(int, index);
0128     QFETCH(QList<int>, result);
0129     for (int i = Qt::NoArrow; i <= Qt::RightArrow; i++) {
0130         QCOMPARE(m_positioner->nearestItem(index, (Qt::ArrowType)i), result[i]);
0131     }
0132 }
0133 
0134 void PositionerTest::tst_isBlank()
0135 {
0136     QCOMPARE(m_positioner->isBlank(0), false);
0137     QCOMPARE(m_positioner->isBlank(11), true);
0138     m_positioner->move({0, 10});
0139     QCOMPARE(m_positioner->isBlank(0), true);
0140 }
0141 
0142 void PositionerTest::tst_reset()
0143 {
0144     ensureFolderModelReady();
0145     m_positioner->move({0, 10});
0146     m_positioner->reset();
0147     QSignalSpy s(m_positioner, &Positioner::positionsChanged);
0148     s.wait(500);
0149     QCOMPARE(s.count(), 1);
0150     checkPositions(3);
0151 
0152     for (int i = 0; i < m_positioner->rowCount(); i++) {
0153         QCOMPARE(m_positioner->map(i), i);
0154     }
0155 }
0156 
0157 void PositionerTest::tst_defaultValues()
0158 {
0159     Positioner positioner;
0160     QVERIFY(!positioner.enabled());
0161     QVERIFY(!positioner.folderModel());
0162     QCOMPARE(positioner.perStripe(), 0);
0163     QVERIFY(positioner.positions().isEmpty());
0164 }
0165 
0166 void PositionerTest::tst_changeEnabledStatus()
0167 {
0168     Positioner positioner;
0169     QVERIFY(!positioner.enabled());
0170     QSignalSpy s(&positioner, &Positioner::enabledChanged);
0171     positioner.setEnabled(true);
0172     QCOMPARE(s.count(), 1);
0173 
0174     positioner.setEnabled(false);
0175     QCOMPARE(s.count(), 2);
0176 
0177     // No change
0178     positioner.setEnabled(false);
0179     QCOMPARE(s.count(), 2);
0180 }
0181 
0182 void PositionerTest::tst_changePerStripe()
0183 {
0184     Positioner positioner;
0185     QCOMPARE(positioner.perStripe(), 0);
0186     QSignalSpy s(&positioner, &Positioner::perStripeChanged);
0187     positioner.setPerStripe(1);
0188     QCOMPARE(s.count(), 1);
0189 
0190     // No change
0191     positioner.setPerStripe(1);
0192     QCOMPARE(s.count(), 1);
0193 
0194     positioner.setPerStripe(4);
0195     QCOMPARE(s.count(), 2);
0196 }
0197 
0198 void PositionerTest::tst_proxyMapping()
0199 {
0200     auto *screenMapper = ScreenMapper::instance();
0201     FolderModel secondFolderModel;
0202     secondFolderModel.classBegin();
0203     secondFolderModel.setUrl(m_folderDir->path() + QDir::separator() + desktop);
0204     secondFolderModel.setUsedByContainment(true);
0205     secondFolderModel.setScreen(1);
0206     secondFolderModel.componentComplete();
0207     Positioner secondPositioner;
0208     secondPositioner.setEnabled(true);
0209     secondPositioner.setFolderModel(&secondFolderModel);
0210     secondPositioner.setPerStripe(3);
0211 
0212     QSignalSpy s2(&secondFolderModel, &FolderModel::listingCompleted);
0213     QVERIFY(s2.wait(1000));
0214 
0215     QHash<int, int> expectedSource2ProxyScreen0;
0216     QHash<int, int> expectedProxy2SourceScreen0;
0217     QHash<int, int> expectedProxy2SourceScreen1;
0218     QHash<int, int> expectedSource2ProxyScreen1;
0219 
0220     for (int i = 0; i < m_folderModel->rowCount(); i++) {
0221         expectedSource2ProxyScreen0[i] = i;
0222         expectedProxy2SourceScreen0[i] = i;
0223     }
0224 
0225     // swap items 1 and 2 in the positioner
0226     m_positioner->move({1, 2, 2, 1});
0227     expectedSource2ProxyScreen0[1] = 2;
0228     expectedSource2ProxyScreen0[2] = 1;
0229     expectedProxy2SourceScreen0[1] = 2;
0230     expectedProxy2SourceScreen0[2] = 1;
0231 
0232     auto savedSource2ProxyScreen0 = expectedSource2ProxyScreen0;
0233     auto savedProxy2SourceScreen0 = expectedProxy2SourceScreen0;
0234 
0235     auto verifyMapping = [](const QHash<int, int> &actual, const QHash<int, int> &expected) {
0236         auto ensureUnique = [](const QHash<int, int> mapping) {
0237             auto values = mapping.values();
0238             std::sort(values.begin(), values.end());
0239             auto uniqueValues = values;
0240             uniqueValues.erase(std::unique(uniqueValues.begin(), uniqueValues.end()), uniqueValues.end());
0241             std::sort(uniqueValues.begin(), uniqueValues.end());
0242             QVERIFY(uniqueValues == values);
0243         };
0244 
0245         ensureUnique(actual);
0246         QCOMPARE(actual, expected);
0247     };
0248 
0249     verifyMapping(m_positioner->proxyToSourceMapping(), expectedProxy2SourceScreen0);
0250     verifyMapping(m_positioner->sourceToProxyMapping(), expectedSource2ProxyScreen0);
0251     verifyMapping(secondPositioner.proxyToSourceMapping(), expectedProxy2SourceScreen1);
0252     verifyMapping(secondPositioner.sourceToProxyMapping(), expectedSource2ProxyScreen1);
0253 
0254     const auto movedItem = m_folderModel->index(1, 0).data(FolderModel::UrlRole).toUrl();
0255 
0256     // move the item 1 from source (now in position 2) to the second screen
0257     screenMapper->addMapping(movedItem, 1, m_currentActivity);
0258 
0259     expectedProxy2SourceScreen1[0] = 0;
0260     expectedSource2ProxyScreen1[0] = 0;
0261     expectedSource2ProxyScreen0.clear();
0262     expectedProxy2SourceScreen0.clear();
0263     for (int i = 0; i < m_folderModel->rowCount(); i++) {
0264         // as item 1 disappeared, the mapping of all items after that are shifted
0265         auto proxyIndex = (i <= 1) ? i : i + 1;
0266         expectedProxy2SourceScreen0[proxyIndex] = i;
0267         expectedSource2ProxyScreen0[i] = proxyIndex;
0268     }
0269 
0270     verifyMapping(m_positioner->proxyToSourceMapping(), expectedProxy2SourceScreen0);
0271     verifyMapping(m_positioner->sourceToProxyMapping(), expectedSource2ProxyScreen0);
0272     verifyMapping(secondPositioner.proxyToSourceMapping(), expectedProxy2SourceScreen1);
0273     verifyMapping(secondPositioner.sourceToProxyMapping(), expectedSource2ProxyScreen1);
0274 
0275     // move back the same item to the first screen
0276     screenMapper->addMapping(movedItem, 0, m_currentActivity);
0277 
0278     // nothing on the second screen
0279     expectedSource2ProxyScreen1.clear();
0280     expectedProxy2SourceScreen1.clear();
0281     // first screen should look like in the beginning
0282     expectedSource2ProxyScreen0 = savedSource2ProxyScreen0;
0283     expectedProxy2SourceScreen0 = savedProxy2SourceScreen0;
0284 
0285     verifyMapping(m_positioner->proxyToSourceMapping(), expectedProxy2SourceScreen0);
0286     verifyMapping(m_positioner->sourceToProxyMapping(), expectedSource2ProxyScreen0);
0287     verifyMapping(secondPositioner.proxyToSourceMapping(), expectedProxy2SourceScreen1);
0288     verifyMapping(secondPositioner.sourceToProxyMapping(), expectedSource2ProxyScreen1);
0289 }
0290 
0291 void PositionerTest::checkPositions(int perStripe)
0292 {
0293     ensureFolderModelReady();
0294 
0295     QSignalSpy s(m_positioner, &Positioner::positionsChanged);
0296     s.wait();
0297 
0298     const auto positions = m_positioner->positions();
0299     struct Pos {
0300         int x;
0301         int y;
0302     };
0303     const auto fileCount = m_folderModel->rowCount();
0304     QHash<QString, Pos> posHash;
0305     QCOMPARE(positions[0].toInt(), 1 + ((fileCount - 1) / perStripe)); // rows
0306     QCOMPARE(positions[1].toInt(), perStripe); // columns
0307     for (int i = 2; i < positions.length() - 2; i += 3) {
0308         posHash[positions[i]] = {positions[i + 1].toInt(), positions[i + 2].toInt()};
0309     }
0310 
0311     int row = 0;
0312     int col = 0;
0313     for (int i = 0; i < fileCount; i++) {
0314         const auto index = m_folderModel->index(i, 0);
0315         const auto url = index.data(FolderModel::UrlRole).toString();
0316         const Pos pos = posHash[url];
0317         QCOMPARE(pos.x, row);
0318         QCOMPARE(pos.y, col);
0319         col++;
0320         if (col == perStripe) {
0321             row++;
0322             col = 0;
0323         }
0324     }
0325 }
0326 
0327 void PositionerTest::ensureFolderModelReady()
0328 {
0329     if (m_folderModel->status() == FolderModel::Listing) {
0330         QSignalSpy folderModelReadySpy(m_folderModel, &FolderModel::statusChanged);
0331         QVERIFY(folderModelReadySpy.wait());
0332     }
0333 }