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 
0004     Work sponsored by the LiMux project of the city of Munich.
0005     SPDX-FileContributor: Andras Mantia <andras.mantia@kdab.com>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #include <array>
0010 
0011 #include "screenmapper.h"
0012 #include "screenmappertest.h"
0013 
0014 #include <QSignalSpy>
0015 #include <QTest>
0016 
0017 #include <PlasmaActivities/Consumer>
0018 
0019 using namespace Qt::StringLiterals;
0020 
0021 QTEST_MAIN(ScreenMapperTest)
0022 
0023 void ScreenMapperTest::initTestCase()
0024 {
0025     m_screenMapper = ScreenMapper::instance();
0026     m_currentActivity = KActivities::Consumer().currentActivity();
0027     m_alternativeActivity = QStringLiteral("a779f58a-b5e3-426d-9994-a8c54d26d528"); // Generated by uuidgen
0028     Q_ASSERT(m_currentActivity != m_alternativeActivity);
0029 }
0030 
0031 void ScreenMapperTest::init()
0032 {
0033     m_screenMapper->cleanup();
0034 }
0035 
0036 void ScreenMapperTest::tst_addScreens()
0037 {
0038     const auto path = ScreenMapper::stringToUrl(QStringLiteral("desktop:/"));
0039     QSignalSpy s(m_screenMapper, &ScreenMapper::screensChanged);
0040     m_screenMapper->addScreen(-1, m_currentActivity, path);
0041     QCOMPARE(s.count(), 0);
0042     m_screenMapper->addScreen(1, m_currentActivity, path);
0043     QCOMPARE(s.count(), 1);
0044     m_screenMapper->addScreen(0, m_currentActivity, path);
0045     QCOMPARE(s.count(), 2);
0046     m_screenMapper->addScreen(1, m_currentActivity, path);
0047     QCOMPARE(s.count(), 2);
0048 
0049     // Test if activity ID works
0050     m_screenMapper->addScreen(1, m_alternativeActivity, path);
0051     QCOMPARE(s.count(), 3);
0052     m_screenMapper->addScreen(3, m_alternativeActivity, path);
0053     QCOMPARE(s.count(), 4);
0054 
0055     QCOMPARE(m_screenMapper->firstAvailableScreen(path, m_currentActivity), 0);
0056     QCOMPARE(m_screenMapper->firstAvailableScreen(path, m_alternativeActivity), 1);
0057 }
0058 
0059 void ScreenMapperTest::tst_removeScreens()
0060 {
0061     const auto path = ScreenMapper::stringToUrl(QStringLiteral("desktop:/"));
0062     addScreens(path, m_currentActivity);
0063     addScreens(path, m_alternativeActivity);
0064     QSignalSpy s(m_screenMapper, &ScreenMapper::screensChanged);
0065     m_screenMapper->removeScreen(-1, m_currentActivity, path);
0066     QCOMPARE(s.count(), 0);
0067     m_screenMapper->removeScreen(1, m_currentActivity, path);
0068     QCOMPARE(s.count(), 1);
0069     QCOMPARE(m_screenMapper->firstAvailableScreen(path, m_currentActivity), 0);
0070     m_screenMapper->removeScreen(1, m_currentActivity, path);
0071     QCOMPARE(s.count(), 1);
0072     m_screenMapper->addScreen(3, m_currentActivity, path);
0073     QCOMPARE(s.count(), 2);
0074     m_screenMapper->removeScreen(0, m_currentActivity, path);
0075     QCOMPARE(s.count(), 3);
0076     QCOMPARE(m_screenMapper->firstAvailableScreen(path, m_currentActivity), 2);
0077 
0078     // Remove screens on m_currentActivity should not affect screens on m_alternativeActivity
0079     QCOMPARE(m_screenMapper->firstAvailableScreen(path, m_alternativeActivity), 0);
0080     m_screenMapper->removeScreen(2, m_alternativeActivity, path);
0081     QCOMPARE(s.count(), 4);
0082 
0083     // Remove screens on m_alternativeActivity should not affect screens on m_currentActivity
0084     QCOMPARE(m_screenMapper->firstAvailableScreen(path, m_currentActivity), 2);
0085 }
0086 
0087 void ScreenMapperTest::tst_addMapping()
0088 {
0089     const auto path = ScreenMapper::stringToUrl(QStringLiteral("desktop:/"));
0090     addScreens(path, m_currentActivity);
0091     addScreens(path, m_alternativeActivity);
0092     QSignalSpy s(m_screenMapper, &ScreenMapper::screenMappingChanged);
0093     QString file(QStringLiteral("desktop:/foo%1.txt"));
0094 
0095     for (int i = 0; i < 3; i++) {
0096         const QUrl url = ScreenMapper::stringToUrl(file.arg(i));
0097         m_screenMapper->addMapping(url, i, m_currentActivity);
0098         QCOMPARE(s.count(), i + 1);
0099         QCOMPARE(m_screenMapper->screenForItem(url, m_currentActivity), i);
0100 
0101         // Mappings added to m_currentActivity should not affect mappings on m_alternativeActivity
0102         QCOMPARE(m_screenMapper->screenForItem(url, m_alternativeActivity), -1);
0103     }
0104 }
0105 
0106 void ScreenMapperTest::tst_addRemoveScreenWithItems()
0107 {
0108     const auto path = ScreenMapper::stringToUrl(QStringLiteral("desktop:/"));
0109     addScreens(path, m_currentActivity);
0110     addScreens(path, m_alternativeActivity);
0111     QString file(QStringLiteral("desktop:/foo%1.txt"));
0112 
0113     for (int i = 0; i < 3; i++) {
0114         const QUrl url = ScreenMapper::stringToUrl(file.arg(i));
0115         for (const QString &activity : {m_currentActivity, m_alternativeActivity}) {
0116             m_screenMapper->addMapping(url, i, activity);
0117         }
0118     }
0119 
0120     // remove one screen
0121     m_screenMapper->removeScreen(1, m_currentActivity, path);
0122     QCOMPARE(m_screenMapper->screenForItem(ScreenMapper::stringToUrl(file.arg(0)), m_currentActivity), 0);
0123     QCOMPARE(m_screenMapper->screenForItem(ScreenMapper::stringToUrl(file.arg(1)), m_currentActivity), -1);
0124     QCOMPARE(m_screenMapper->screenForItem(ScreenMapper::stringToUrl(file.arg(2)), m_currentActivity), 2);
0125     // Remove a screen of m_currentActivity should not affect screens of m_alternativeActivity
0126     QCOMPARE(m_screenMapper->screenForItem(ScreenMapper::stringToUrl(file.arg(1)), m_alternativeActivity), 1);
0127 
0128     // add removed screen back, items screen is restored
0129     m_screenMapper->addScreen(1, m_currentActivity, path);
0130     QCOMPARE(m_screenMapper->screenForItem(ScreenMapper::stringToUrl(file.arg(0)), m_currentActivity), 0);
0131     QCOMPARE(m_screenMapper->screenForItem(ScreenMapper::stringToUrl(file.arg(1)), m_currentActivity), 1);
0132     QCOMPARE(m_screenMapper->screenForItem(ScreenMapper::stringToUrl(file.arg(2)), m_currentActivity), 2);
0133     // Restore a screen of m_currentActivity should not affect screens of m_alternativeActivity
0134     QCOMPARE(m_screenMapper->screenForItem(ScreenMapper::stringToUrl(file.arg(1)), m_alternativeActivity), 1);
0135 
0136     // remove all screens, firstAvailableScreen changes
0137     m_screenMapper->removeScreen(0, m_currentActivity, path);
0138     QCOMPARE(m_screenMapper->firstAvailableScreen(path, m_currentActivity), 1);
0139     m_screenMapper->removeScreen(1, m_currentActivity, path);
0140     QCOMPARE(m_screenMapper->firstAvailableScreen(path, m_currentActivity), 2);
0141     m_screenMapper->removeScreen(2, m_currentActivity, path);
0142     QCOMPARE(m_screenMapper->firstAvailableScreen(path, m_currentActivity), -1);
0143     // Remove screens of m_currentActivity should not affect screens of m_alternativeActivity
0144     QCOMPARE(m_screenMapper->firstAvailableScreen(path, m_alternativeActivity), 0);
0145 
0146     QCOMPARE(m_screenMapper->screenForItem(ScreenMapper::stringToUrl(file.arg(0)), m_currentActivity), -1);
0147     QCOMPARE(m_screenMapper->screenForItem(ScreenMapper::stringToUrl(file.arg(1)), m_currentActivity), -1);
0148     QCOMPARE(m_screenMapper->screenForItem(ScreenMapper::stringToUrl(file.arg(2)), m_currentActivity), -1);
0149 
0150     // add all screens back, all item's screen is restored
0151     addScreens(path, m_currentActivity);
0152     QCOMPARE(m_screenMapper->screenForItem(ScreenMapper::stringToUrl(file.arg(0)), m_currentActivity), 0);
0153     QCOMPARE(m_screenMapper->screenForItem(ScreenMapper::stringToUrl(file.arg(1)), m_currentActivity), 1);
0154     QCOMPARE(m_screenMapper->screenForItem(ScreenMapper::stringToUrl(file.arg(2)), m_currentActivity), 2);
0155 
0156     // remove one screen and move its item
0157     const QUrl movedItem = ScreenMapper::stringToUrl(file.arg(1));
0158     m_screenMapper->removeScreen(1, m_currentActivity, path);
0159     QCOMPARE(m_screenMapper->screenForItem(movedItem, m_currentActivity), -1);
0160     m_screenMapper->addMapping(movedItem, 0, m_currentActivity);
0161     QCOMPARE(m_screenMapper->screenForItem(movedItem, m_currentActivity), 0);
0162     QCOMPARE(m_screenMapper->screenForItem(movedItem, m_alternativeActivity), 1);
0163 
0164     // add back the screen, item goes back to the original place
0165     m_screenMapper->addScreen(1, m_currentActivity, path);
0166     QCOMPARE(m_screenMapper->screenForItem(movedItem, m_currentActivity), 1);
0167     QCOMPARE(m_screenMapper->screenForItem(movedItem, m_alternativeActivity), 1);
0168 }
0169 
0170 void ScreenMapperTest::tst_addRemoveScreenDifferentPaths()
0171 {
0172     const auto path = ScreenMapper::stringToUrl(QStringLiteral("desktop:/Foo"));
0173     const auto path2 = ScreenMapper::stringToUrl(QStringLiteral("desktop:/Foo2"));
0174     m_screenMapper->addScreen(0, m_currentActivity, path);
0175     QCOMPARE(m_screenMapper->firstAvailableScreen(path, m_currentActivity), 0);
0176     QCOMPARE(m_screenMapper->firstAvailableScreen(path2, m_currentActivity), -1);
0177 }
0178 
0179 void ScreenMapperTest::tst_readScreenActivityMapping()
0180 {
0181     const auto path = ScreenMapper::stringToUrl(QStringLiteral("desktop:/"));
0182     addScreens(path, m_currentActivity);
0183     addScreens(path, m_alternativeActivity);
0184 
0185     QString file(QStringLiteral("desktop:/foo%1.txt"));
0186     const QStringList paths{
0187         file.arg(0),
0188         file.arg(1),
0189     };
0190     // clang-format off
0191     const QStringList mapping{
0192         paths[0], "0"_L1, m_currentActivity,
0193         paths[1], "1"_L1, m_currentActivity,
0194         paths[0], "1"_L1, m_alternativeActivity,
0195     };
0196     // clang-format on
0197     QSignalSpy s(m_screenMapper, &ScreenMapper::screenMappingChanged);
0198 
0199     m_screenMapper->setScreenMapping(mapping);
0200     QCOMPARE(s.count(), 1);
0201 
0202     // Check if the config is loaded correctly
0203     QCOMPARE(m_screenMapper->screenForItem(ScreenMapper::stringToUrl(paths[0]), m_currentActivity), 0);
0204     QCOMPARE(m_screenMapper->screenForItem(ScreenMapper::stringToUrl(paths[1]), m_currentActivity), 1);
0205     QCOMPARE(m_screenMapper->screenForItem(ScreenMapper::stringToUrl(paths[0]), m_alternativeActivity), 1);
0206     QCOMPARE(m_screenMapper->screenForItem(ScreenMapper::stringToUrl(paths[1]), m_alternativeActivity), -1);
0207 }
0208 
0209 void ScreenMapperTest::tst_saveScreenActivityMapping()
0210 {
0211     const auto path = ScreenMapper::stringToUrl(QStringLiteral("desktop:/"));
0212     addScreens(path, m_currentActivity);
0213     addScreens(path, m_alternativeActivity);
0214 
0215     QString file(QStringLiteral("desktop:/foo%1.txt"));
0216     const std::array<QStringList, 3> expectedMapping{
0217         QStringList{file.arg(0), "0"_L1, m_currentActivity},
0218         QStringList{file.arg(1), "1"_L1, m_currentActivity},
0219         QStringList{file.arg(0), "1"_L1, m_alternativeActivity},
0220     };
0221 
0222     for (const auto &l : expectedMapping) {
0223         m_screenMapper->addMapping(ScreenMapper::stringToUrl(l[0]), l[1].toInt(), l[2]);
0224     }
0225 
0226     const QStringList mapping = m_screenMapper->screenMapping();
0227 
0228     QCOMPARE(mapping.count(), expectedMapping.size() * expectedMapping[0].size());
0229 
0230     for (int i = 0; i < mapping.count() - (expectedMapping[0].size() - 1); i += expectedMapping[0].size()) {
0231         const QStringList configGroup{mapping[i], mapping[i + 1], mapping[i + 2]};
0232         QVERIFY(std::find(std::cbegin(expectedMapping), std::cend(expectedMapping), configGroup) != std::cend(expectedMapping));
0233     }
0234 }
0235 
0236 void ScreenMapperTest::tst_readAndSaveItemsOnActivitiesOnDisabledScreens()
0237 {
0238     QString file(QStringLiteral("desktop:/foo%1.txt"));
0239     const QStringList paths{
0240         ScreenMapper::stringToUrl(file.arg(0)).toString(),
0241         ScreenMapper::stringToUrl(file.arg(1)).toString(),
0242         ScreenMapper::stringToUrl(file.arg(2)).toString(),
0243         ScreenMapper::stringToUrl(file.arg(3)).toString(),
0244     };
0245     // clang-format off
0246     const std::array<QStringList, 3> expectedMapping{
0247         QStringList{"0"_L1, m_currentActivity, "2"_L1, paths[0], paths[1]},
0248         QStringList{"1"_L1, m_currentActivity, "2"_L1, paths[2], paths[3]},
0249         QStringList{"0"_L1, m_alternativeActivity, "2"_L1, paths[0], paths[1]},
0250     };
0251     // clang-format on
0252     QStringList seralizedMap;
0253 
0254     // Create a seralized QStringList from expectedMapping
0255     for (const auto &l : expectedMapping) {
0256         for (const auto &s : l) {
0257             seralizedMap.append(s);
0258         }
0259     }
0260 
0261     m_screenMapper->readDisabledScreensMap(seralizedMap);
0262 
0263     // Check the actual mapping result
0264     const QStringList mapping = m_screenMapper->disabledScreensMap();
0265     qDebug() << "mapping:" << mapping;
0266 
0267     QCOMPARE(mapping.count(), expectedMapping.size() * expectedMapping[0].size());
0268 
0269     for (int i = 0; i < mapping.count() - (expectedMapping[0].size() - 1); i += expectedMapping[0].size()) {
0270         const QStringList configGroup{mapping[i], mapping[i + 1], mapping[i + 2], mapping[i + 3], mapping[i + 4]};
0271         bool matched = false;
0272         for (const QStringList &expectedConfigGroup : expectedMapping) {
0273             if (expectedConfigGroup[0] == mapping[i] && expectedConfigGroup[1] == mapping[i + 1]) {
0274                 QVERIFY(expectedConfigGroup[2] == mapping[i + 2]);
0275                 for (int j = 3; j < 3 + mapping[i + 2].toInt(); ++j) {
0276                     QVERIFY(std::find(expectedConfigGroup.cbegin(), expectedConfigGroup.cend(), mapping[i + j]) != expectedConfigGroup.cend());
0277                 }
0278                 matched = true;
0279                 break;
0280             }
0281         }
0282         QVERIFY(matched);
0283     }
0284 }
0285 
0286 void ScreenMapperTest::addScreens(const QUrl &path, const QString &activity)
0287 {
0288     for (int screen = 0; screen < 3; screen++) {
0289         m_screenMapper->addScreen(screen, activity, path);
0290     }
0291 }