File indexing completed on 2024-05-12 17:10:21
0001 /* 0002 SPDX-FileCopyrightText: 2016 Marco Martin <mart@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 0007 #include <QObject> 0008 0009 #include <QApplication> 0010 #include <QDir> 0011 #include <QScreen> 0012 #include <QSignalSpy> 0013 #include <QStandardPaths> 0014 #include <QTemporaryDir> 0015 #include <QTest> 0016 0017 #include "../screenpool.h" 0018 #include "mockcompositor.h" 0019 #include "xdgoutputv1.h" 0020 0021 #include "config-KDECI_BUILD.h" 0022 0023 using namespace MockCompositor; 0024 0025 class ScreenPoolTest : public QObject, DefaultCompositor 0026 { 0027 Q_OBJECT 0028 0029 private Q_SLOTS: 0030 void initTestCase(); 0031 void cleanupTestCase(); 0032 0033 void testScreenInsertion(); 0034 void testRedundantScreenInsertion(); 0035 void testMoveOutOfRedundant(); 0036 void testMoveInRedundant(); 0037 void testOrderSwap(); 0038 void testPrimarySwapToRedundant(); 0039 void testMoveRedundantToMakePrimary(); 0040 void testMoveInRedundantToLosePrimary(); 0041 void testSecondScreenRemoval(); 0042 void testThirdScreenRemoval(); 0043 void testLastScreenRemoval(); 0044 void testFakeToRealScreen(); 0045 0046 private: 0047 ScreenPool *m_screenPool; 0048 }; 0049 0050 void ScreenPoolTest::initTestCase() 0051 { 0052 QStandardPaths::setTestModeEnabled(true); 0053 qRegisterMetaType<QScreen *>(); 0054 0055 m_screenPool = new ScreenPool(this); 0056 0057 QTRY_COMPARE(QGuiApplication::screens().size(), 1); 0058 QTRY_COMPARE(m_screenPool->screenOrder().size(), 1); 0059 QCOMPARE(QGuiApplication::screens().first()->name(), QStringLiteral("WL-1")); 0060 QCOMPARE(QGuiApplication::primaryScreen(), QGuiApplication::screens().first()); 0061 QCOMPARE(QGuiApplication::primaryScreen(), m_screenPool->primaryScreen()); 0062 QCOMPARE(m_screenPool->idForScreen(m_screenPool->primaryScreen()), 0); 0063 QCOMPARE(m_screenPool->screenForId(0)->name(), QStringLiteral("WL-1")); 0064 } 0065 0066 void ScreenPoolTest::cleanupTestCase() 0067 { 0068 #if !KDECI_BUILD 0069 QCOMPOSITOR_COMPARE(getAll<Output>().size(), 1); // Only the default output should be left 0070 QTRY_COMPARE(QGuiApplication::screens().size(), 1); 0071 QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage())); 0072 #endif 0073 0074 KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("ScreenConnectors")); 0075 cg.deleteGroup(); 0076 cg.sync(); 0077 } 0078 0079 void ScreenPoolTest::testScreenInsertion() 0080 { 0081 QSignalSpy addedSpy(qGuiApp, SIGNAL(screenAdded(QScreen *))); 0082 QSignalSpy orderChangeSpy(m_screenPool, &ScreenPool::screenOrderChanged); 0083 0084 // Add a new output 0085 exec([=] { 0086 OutputData data; 0087 data.mode.resolution = {1920, 1080}; 0088 data.position = {1920, 0}; 0089 data.physicalSize = data.mode.physicalSizeForDpi(96); 0090 // NOTE: assumes that when a screen is added it will already have the final geometry 0091 add<Output>(data); 0092 outputOrder()->setList({"WL-1", "WL-2"}); 0093 }); 0094 0095 addedSpy.wait(); 0096 0097 orderChangeSpy.wait(); 0098 0099 QCOMPARE(orderChangeSpy.size(), 1); 0100 QCOMPARE(QGuiApplication::screens().size(), 2); 0101 QCOMPARE(m_screenPool->screenOrder().size(), 2); 0102 QCOMPARE(addedSpy.size(), 1); 0103 0104 QScreen *newScreen = addedSpy.takeFirst().at(0).value<QScreen *>(); 0105 QCOMPARE(newScreen->name(), QStringLiteral("WL-2")); 0106 QCOMPARE(newScreen->geometry(), QRect(1920, 0, 1920, 1080)); 0107 // Check mapping 0108 QCOMPARE(m_screenPool->idForScreen(newScreen), 1); 0109 QCOMPARE(m_screenPool->screenForId(1)->name(), QStringLiteral("WL-2")); 0110 } 0111 0112 void ScreenPoolTest::testRedundantScreenInsertion() 0113 { 0114 QSignalSpy orderChangeSpy(m_screenPool, &ScreenPool::screenOrderChanged); 0115 QSignalSpy addedFromAppSpy(qGuiApp, SIGNAL(screenAdded(QScreen *))); 0116 0117 // Add a new output 0118 exec([=] { 0119 OutputData data; 0120 data.mode.resolution = {1280, 720}; 0121 data.position = {1920, 0}; 0122 data.physicalSize = data.mode.physicalSizeForDpi(96); 0123 // NOTE: assumes that when a screen is added it will already have the final geometry 0124 add<Output>(data); 0125 outputOrder()->setList({"WL-1", "WL-2", "WL-3"}); 0126 }); 0127 0128 addedFromAppSpy.wait(); 0129 orderChangeSpy.wait(250); 0130 // only addedFromAppSpy will have registered something, nothing in orderChangeSpy, 0131 // on ScreenPool API POV is like this new screen doesn't exist, because is redundant to WL-2 0132 QCOMPARE(QGuiApplication::screens().size(), 3); 0133 QCOMPARE(m_screenPool->screenOrder().size(), 2); 0134 QCOMPARE(addedFromAppSpy.size(), 1); 0135 QCOMPARE(orderChangeSpy.size(), 0); 0136 0137 QScreen *newScreen = addedFromAppSpy.takeFirst().at(0).value<QScreen *>(); 0138 QCOMPARE(newScreen->name(), QStringLiteral("WL-3")); 0139 QCOMPARE(newScreen->geometry(), QRect(1920, 0, 1280, 720)); 0140 QVERIFY(!m_screenPool->screenOrder().contains(newScreen)); 0141 0142 QCOMPARE(m_screenPool->idForScreen(newScreen), -1); 0143 } 0144 0145 void ScreenPoolTest::testMoveOutOfRedundant() 0146 { 0147 QSignalSpy orderChangeSpy(m_screenPool, &ScreenPool::screenOrderChanged); 0148 0149 exec([=] { 0150 auto *out = output(2); 0151 auto *xdgOut = xdgOutput(out); 0152 out->m_data.mode.resolution = {1280, 2048}; 0153 xdgOut->sendLogicalSize(QSize(1280, 2048)); 0154 out->sendDone(); 0155 outputOrder()->setList({"WL-1", "WL-2", "WL-3"}); 0156 }); 0157 0158 orderChangeSpy.wait(); 0159 QCOMPARE(orderChangeSpy.size(), 1); 0160 0161 QList<QScreen *> newOrder = orderChangeSpy[0].at(0).value<QList<QScreen *>>(); 0162 QCOMPARE(newOrder.count(), 3); 0163 QCOMPARE(newOrder[1]->name(), QStringLiteral("WL-2")); 0164 QCOMPARE(newOrder[1]->geometry(), QRect(1920, 0, 1920, 1080)); 0165 QVERIFY(m_screenPool->screenOrder().contains(newOrder[0])); 0166 } 0167 0168 void ScreenPoolTest::testMoveInRedundant() 0169 { 0170 QSignalSpy removedSpy(m_screenPool, SIGNAL(screenRemoved(QScreen *))); 0171 0172 exec([=] { 0173 auto *out = output(2); 0174 auto *xdgOut = xdgOutput(out); 0175 out->m_data.mode.resolution = {1280, 720}; 0176 xdgOut->sendLogicalSize(QSize(1280, 720)); 0177 out->sendDone(); 0178 outputOrder()->setList({"WL-1", "WL-2", "WL-3"}); 0179 }); 0180 0181 removedSpy.wait(); 0182 QCOMPARE(removedSpy.size(), 1); 0183 QScreen *oldScreen = removedSpy.takeFirst().at(0).value<QScreen *>(); 0184 QCOMPARE(oldScreen->name(), QStringLiteral("WL-3")); 0185 QCOMPARE(oldScreen->geometry(), QRect(1920, 0, 1280, 720)); 0186 QVERIFY(!m_screenPool->screenOrder().contains(oldScreen)); 0187 } 0188 0189 void ScreenPoolTest::testOrderSwap() 0190 { 0191 QSignalSpy orderChangeSpy(m_screenPool, &ScreenPool::screenOrderChanged); 0192 0193 // Check ScreenPool mapping before switch 0194 QList<QScreen *> oldOrder = m_screenPool->screenOrder(); 0195 QCOMPARE(oldOrder.count(), 2); 0196 QCOMPARE(oldOrder[0]->name(), QStringLiteral("WL-1")); 0197 QCOMPARE(oldOrder[1]->name(), QStringLiteral("WL-2")); 0198 0199 QCOMPARE(m_screenPool->screenOrder()[0]->name(), QStringLiteral("WL-1")); 0200 QCOMPARE(m_screenPool->screenOrder()[1]->name(), QStringLiteral("WL-2")); 0201 0202 // Set a primary screen 0203 // TODO: "WL-2", "WL-1", "WL-3" when tests are self contained 0204 exec([=] { 0205 outputOrder()->setList({"WL-2", "WL-1", "WL-3"}); 0206 }); 0207 0208 orderChangeSpy.wait(); 0209 0210 QCOMPARE(orderChangeSpy.size(), 1); 0211 QList<QScreen *> newOrder = orderChangeSpy[0].at(0).value<QList<QScreen *>>(); 0212 QCOMPARE(newOrder.count(), 2); 0213 QCOMPARE(newOrder[0], oldOrder[1]); 0214 QCOMPARE(newOrder[1], oldOrder[0]); 0215 0216 QCOMPARE(newOrder[1]->name(), QStringLiteral("WL-1")); 0217 QCOMPARE(newOrder[1]->geometry(), QRect(0, 0, 1920, 1080)); 0218 QCOMPARE(newOrder[0]->name(), QStringLiteral("WL-2")); 0219 QCOMPARE(newOrder[0]->geometry(), QRect(1920, 0, 1920, 1080)); 0220 0221 // Check ScreenPool mapping 0222 QCOMPARE(newOrder[0]->name(), QStringLiteral("WL-2")); 0223 QCOMPARE(m_screenPool->primaryScreen()->name(), newOrder[0]->name()); 0224 QCOMPARE(m_screenPool->idForScreen(newOrder[0]), 0); 0225 QCOMPARE(m_screenPool->idForScreen(newOrder[1]), 1); 0226 } 0227 0228 void ScreenPoolTest::testPrimarySwapToRedundant() 0229 { 0230 QSignalSpy orderChangeSpy(m_screenPool, &ScreenPool::screenOrderChanged); 0231 0232 // Set a primary screen 0233 exec([=] { 0234 outputOrder()->setList({"WL-3", "WL-2", "WL-1"}); 0235 }); 0236 0237 orderChangeSpy.wait(250); 0238 // Nothing will happen, is like WL3 doesn't exist 0239 QCOMPARE(orderChangeSpy.size(), 0); 0240 } 0241 0242 void ScreenPoolTest::testMoveRedundantToMakePrimary() 0243 { 0244 QSignalSpy orderChangeSpy(m_screenPool, &ScreenPool::screenOrderChanged); 0245 0246 exec([=] { 0247 auto *out = output(2); 0248 auto *xdgOut = xdgOutput(out); 0249 out->m_data.mode.resolution = {1280, 2048}; 0250 xdgOut->sendLogicalSize(QSize(1280, 2048)); 0251 out->sendDone(); 0252 outputOrder()->setList({"WL-3", "WL-2", "WL-1"}); 0253 }); 0254 0255 QTRY_COMPARE(orderChangeSpy.size(), 1); 0256 /*QScreen *newScreen = addedSpy.takeFirst().at(0).value<QScreen *>(); 0257 QCOMPARE(newScreen->name(), QStringLiteral("WL-3")); 0258 QCOMPARE(newScreen->geometry(), QRect(1920, 0, 1280, 2048)); 0259 QVERIFY(m_screenPool->screens().contains(newScreen));*/ 0260 0261 // Test the new primary 0262 QList<QScreen *> newOrder = orderChangeSpy[0].at(0).value<QList<QScreen *>>(); 0263 QCOMPARE(newOrder.size(), 3); 0264 QCOMPARE(newOrder[0]->name(), QStringLiteral("WL-3")); 0265 QCOMPARE(newOrder[0]->geometry(), QRect(1920, 0, 1280, 2048)); 0266 QCOMPARE(newOrder[1]->name(), QStringLiteral("WL-2")); 0267 QCOMPARE(newOrder[1]->geometry(), QRect(1920, 0, 1920, 1080)); 0268 QCOMPARE(newOrder[2]->name(), QStringLiteral("WL-1")); 0269 QCOMPARE(newOrder[2]->geometry(), QRect(0, 0, 1920, 1080)); 0270 0271 // Check ScreenPool mapping 0272 QCOMPARE(m_screenPool->primaryScreen()->name(), QStringLiteral("WL-3")); 0273 QCOMPARE(m_screenPool->idForScreen(newOrder[0]), 0); 0274 QCOMPARE(m_screenPool->idForScreen(newOrder[1]), 1); 0275 } 0276 0277 void ScreenPoolTest::testMoveInRedundantToLosePrimary() 0278 { 0279 QSignalSpy orderChangeSpy(m_screenPool, &ScreenPool::screenOrderChanged); 0280 0281 QCOMPARE(m_screenPool->screenOrder().size(), 3); 0282 QScreen *oldScreen = m_screenPool->screenOrder()[0]; 0283 0284 exec([=] { 0285 auto *out = output(2); 0286 auto *xdgOut = xdgOutput(out); 0287 xdgOut->sendLogicalSize(QSize(1280, 720)); 0288 out->m_data.mode.resolution = {1280, 720}; 0289 out->sendDone(); 0290 outputOrder()->setList({"WL-3", "WL-2", "WL-1"}); 0291 }); 0292 0293 QTRY_COMPARE(orderChangeSpy.size(), 1); 0294 QList<QScreen *> newOrder = orderChangeSpy[0].at(0).value<QList<QScreen *>>(); 0295 QCOMPARE(newOrder.size(), 2); 0296 0297 QCOMPARE(newOrder[1]->name(), QStringLiteral("WL-1")); 0298 QCOMPARE(newOrder[1]->geometry(), QRect(0, 0, 1920, 1080)); 0299 QCOMPARE(newOrder[0]->name(), QStringLiteral("WL-2")); 0300 QCOMPARE(newOrder[0]->geometry(), QRect(1920, 0, 1920, 1080)); 0301 0302 // Check ScreenPool mapping 0303 QCOMPARE(newOrder[0]->name(), QStringLiteral("WL-2")); 0304 QCOMPARE(m_screenPool->primaryScreen()->name(), newOrder[0]->name()); 0305 QCOMPARE(m_screenPool->idForScreen(newOrder[0]), 0); 0306 QCOMPARE(m_screenPool->idForScreen(newOrder[1]), 1); 0307 0308 QVERIFY(!newOrder.contains(oldScreen)); 0309 QCOMPARE(oldScreen->name(), QStringLiteral("WL-3")); 0310 QCOMPARE(oldScreen->geometry(), QRect(1920, 0, 1280, 720)); 0311 QVERIFY(!m_screenPool->screenOrder().contains(oldScreen)); 0312 } 0313 0314 void ScreenPoolTest::testSecondScreenRemoval() 0315 { 0316 QSignalSpy orderChangeSpy(m_screenPool, &ScreenPool::screenOrderChanged); 0317 QSignalSpy removedSpy(m_screenPool, SIGNAL(screenRemoved(QScreen *))); 0318 0319 // Check ScreenPool mapping before switch 0320 QCOMPARE(m_screenPool->screenOrder()[0]->name(), QStringLiteral("WL-2")); 0321 QCOMPARE(m_screenPool->screenOrder()[1]->name(), QStringLiteral("WL-1")); 0322 0323 // Remove an output 0324 exec([=] { 0325 remove(output(1)); 0326 outputOrder()->setList({"WL-3", "WL-1"}); 0327 }); 0328 0329 // Removing the primary screen, will change a primaryChange signal beforehand 0330 QTRY_COMPARE(orderChangeSpy.size(), 1); 0331 QCOMPARE(orderChangeSpy.size(), 1); 0332 QList<QScreen *> newOrder = orderChangeSpy[0].at(0).value<QList<QScreen *>>(); 0333 QCOMPARE(newOrder.size(), 2); 0334 QCOMPARE(newOrder[0]->name(), QStringLiteral("WL-3")); 0335 QCOMPARE(newOrder[0]->geometry(), QRect(1920, 0, 1280, 720)); 0336 QCOMPARE(newOrder[1]->name(), QStringLiteral("WL-1")); 0337 QCOMPARE(newOrder[1]->geometry(), QRect(0, 0, 1920, 1080)); 0338 } 0339 0340 void ScreenPoolTest::testThirdScreenRemoval() 0341 { 0342 QSignalSpy removedSpy(m_screenPool, SIGNAL(screenRemoved(QScreen *))); 0343 QSignalSpy orderChangeSpy(m_screenPool, &ScreenPool::screenOrderChanged); 0344 0345 // Remove an output 0346 exec([=] { 0347 remove(output(1)); 0348 outputOrder()->setList({"WL-1"}); 0349 }); 0350 0351 // NOTE: we can neither access the data of removedSpy nor oldPrimary because at this point will be dangling 0352 removedSpy.wait(); 0353 QTRY_COMPARE(orderChangeSpy.size(), 1); 0354 0355 QCOMPARE(orderChangeSpy.size(), 1); 0356 QList<QScreen *> newOrder = orderChangeSpy[0].at(0).value<QList<QScreen *>>(); 0357 QCOMPARE(newOrder.size(), 1); 0358 QCOMPARE(QGuiApplication::screens().size(), 1); 0359 QCOMPARE(m_screenPool->screenOrder().size(), 1); 0360 QScreen *lastScreen = m_screenPool->screenOrder().first(); 0361 QCOMPARE(lastScreen->name(), QStringLiteral("WL-1")); 0362 QCOMPARE(lastScreen->geometry(), QRect(0, 0, 1920, 1080)); 0363 QCOMPARE(m_screenPool->screenOrder().first(), lastScreen); 0364 // This shouldn't have changed after removing a non primary screen 0365 QCOMPARE(m_screenPool->screenOrder()[0]->name(), QStringLiteral("WL-1")); 0366 } 0367 0368 void ScreenPoolTest::testLastScreenRemoval() 0369 { 0370 QSignalSpy removedSpy(m_screenPool, SIGNAL(screenRemoved(QScreen *))); 0371 0372 // Remove an output 0373 exec([=] { 0374 remove(output(0)); 0375 outputOrder()->setList({}); 0376 }); 0377 0378 // NOTE: we can neither access the data of removedSpy nor oldPrimary because at this point will be dangling 0379 removedSpy.wait(); 0380 0381 QCOMPARE(QGuiApplication::screens().size(), 1); 0382 QCOMPARE(m_screenPool->screenOrder().size(), 0); 0383 QScreen *fakeScreen = QGuiApplication::screens().first(); 0384 QCOMPARE(fakeScreen->name(), QString()); 0385 QCOMPARE(fakeScreen->geometry(), QRect(0, 0, 0, 0)); 0386 } 0387 0388 void ScreenPoolTest::testFakeToRealScreen() 0389 { 0390 #if KDECI_BUILD 0391 QSKIP("testFakeToRealScreen causes assertion error in screenpool.cpp line 320"); 0392 #endif 0393 0394 QSignalSpy orderChangeSpy(m_screenPool, &ScreenPool::screenOrderChanged); 0395 0396 // Add a new output 0397 exec([=] { 0398 OutputData data; 0399 data.mode.resolution = {1920, 1080}; 0400 data.position = {0, 0}; 0401 data.physicalSize = data.mode.physicalSizeForDpi(96); 0402 auto *out = add<Output>(data); 0403 auto *xdgOut = xdgOutput(out); 0404 xdgOut->m_name = QStringLiteral("WL-1"); 0405 outputOrder()->setList({"WL-1"}); 0406 }); 0407 0408 orderChangeSpy.wait(); 0409 QList<QScreen *> newOrder = orderChangeSpy[0].at(0).value<QList<QScreen *>>(); 0410 QCOMPARE(newOrder.size(), 1); 0411 QCOMPARE(QGuiApplication::screens().size(), 1); 0412 QCOMPARE(m_screenPool->screenOrder().size(), 1); 0413 0414 QScreen *newScreen = newOrder[0]; 0415 QCOMPARE(newScreen, QGuiApplication::screens()[0]); 0416 QCOMPARE(newScreen->name(), QStringLiteral("WL-1")); 0417 QCOMPARE(newScreen->geometry(), QRect(0, 0, 1920, 1080)); 0418 0419 QCOMPARE(m_screenPool->idForScreen(newScreen), 0); 0420 } 0421 0422 QCOMPOSITOR_TEST_MAIN(ScreenPoolTest) 0423 0424 #include "screenpooltest.moc"