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