You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
416 lines
15 KiB
416 lines
15 KiB
/* |
|
SPDX-FileCopyrightText: 2016 Marco Martin <mart@kde.org> |
|
|
|
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL |
|
*/ |
|
|
|
#include <QObject> |
|
|
|
#include <QDir> |
|
#include <QScreen> |
|
#include <QSignalSpy> |
|
#include <QStandardPaths> |
|
#include <QTemporaryDir> |
|
#include <QTest> |
|
|
|
#include "../screenpool.h" |
|
#include "mockcompositor.h" |
|
#include "xdgoutputv1.h" |
|
|
|
using namespace MockCompositor; |
|
|
|
class ScreenPoolTest : public QObject, DefaultCompositor |
|
{ |
|
Q_OBJECT |
|
|
|
private Q_SLOTS: |
|
void initTestCase(); |
|
void cleanupTestCase(); |
|
|
|
void testScreenInsertion(); |
|
void testRedundantScreenInsertion(); |
|
void testMoveOutOfRedundant(); |
|
void testMoveInRedundant(); |
|
void testPrimarySwap(); |
|
void testPrimarySwapToRedundant(); |
|
void testMoveRedundantToMakePrimary(); |
|
void testMoveInRedundantToLosePrimary(); |
|
void testSecondScreenRemoval(); |
|
void testThirdScreenRemoval(); |
|
void testLastScreenRemoval(); |
|
void testFakeToRealScreen(); |
|
|
|
private: |
|
ScreenPool *m_screenPool; |
|
}; |
|
|
|
void ScreenPoolTest::initTestCase() |
|
{ |
|
QStandardPaths::setTestModeEnabled(true); |
|
qRegisterMetaType<QScreen *>(); |
|
|
|
KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("ScreenConnectors")); |
|
cg.deleteGroup(); |
|
cg.sync(); |
|
m_screenPool = new ScreenPool(KSharedConfig::openConfig(), this); |
|
m_screenPool->load(); |
|
|
|
QTRY_COMPARE(QGuiApplication::screens().size(), 1); |
|
QCOMPARE(m_screenPool->screens().size(), 1); |
|
QCOMPARE(QGuiApplication::screens().first()->name(), QStringLiteral("WL-1")); |
|
QCOMPARE(QGuiApplication::primaryScreen(), QGuiApplication::screens().first()); |
|
QCOMPARE(QGuiApplication::primaryScreen(), m_screenPool->primaryScreen()); |
|
QCOMPARE(m_screenPool->id(m_screenPool->primaryScreen()->name()), 0); |
|
QCOMPARE(m_screenPool->connector(0), QStringLiteral("WL-1")); |
|
} |
|
|
|
void ScreenPoolTest::cleanupTestCase() |
|
{ |
|
QCOMPOSITOR_COMPARE(getAll<Output>().size(), 1); // Only the default output should be left |
|
QTRY_COMPARE(QGuiApplication::screens().size(), 1); |
|
QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage())); |
|
|
|
KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("ScreenConnectors")); |
|
cg.deleteGroup(); |
|
cg.sync(); |
|
} |
|
|
|
void ScreenPoolTest::testScreenInsertion() |
|
{ |
|
QSignalSpy addedSpy(m_screenPool, SIGNAL(screenAdded(QScreen *))); |
|
|
|
// Add a new output |
|
exec([=] { |
|
OutputData data; |
|
data.mode.resolution = {1920, 1080}; |
|
data.position = {1920, 0}; |
|
data.physicalSize = data.mode.physicalSizeForDpi(96); |
|
// NOTE: assumes that when a screen is added it will already have the final geometry |
|
add<Output>(data); |
|
}); |
|
|
|
addedSpy.wait(); |
|
QCOMPARE(QGuiApplication::screens().size(), 2); |
|
QCOMPARE(m_screenPool->screens().size(), 2); |
|
QCOMPARE(addedSpy.size(), 1); |
|
|
|
QScreen *newScreen = addedSpy.takeFirst().at(0).value<QScreen *>(); |
|
QCOMPARE(newScreen->name(), QStringLiteral("WL-2")); |
|
QCOMPARE(newScreen->geometry(), QRect(1920, 0, 1920, 1080)); |
|
// Check mapping |
|
QCOMPARE(m_screenPool->id(newScreen->name()), 1); |
|
QCOMPARE(m_screenPool->connector(1), QStringLiteral("WL-2")); |
|
} |
|
|
|
void ScreenPoolTest::testRedundantScreenInsertion() |
|
{ |
|
QSignalSpy addedSpy(m_screenPool, SIGNAL(screenAdded(QScreen *))); |
|
QSignalSpy addedFromAppSpy(qGuiApp, SIGNAL(screenAdded(QScreen *))); |
|
|
|
// Add a new output |
|
exec([=] { |
|
OutputData data; |
|
data.mode.resolution = {1280, 720}; |
|
data.position = {1920, 0}; |
|
data.physicalSize = data.mode.physicalSizeForDpi(96); |
|
// NOTE: assumes that when a screen is added it will already have the final geometry |
|
add<Output>(data); |
|
}); |
|
|
|
addedFromAppSpy.wait(); |
|
addedSpy.wait(250); |
|
// only addedFromAppSpy will have registered something, nothing in addedSpy, |
|
// on ScreenPool API POV is like this new screen doesn't exist, because is redundant to WL-2 |
|
QCOMPARE(QGuiApplication::screens().size(), 3); |
|
QCOMPARE(m_screenPool->screens().size(), 2); |
|
QCOMPARE(addedFromAppSpy.size(), 1); |
|
QCOMPARE(addedSpy.size(), 0); |
|
|
|
QScreen *newScreen = addedFromAppSpy.takeFirst().at(0).value<QScreen *>(); |
|
QCOMPARE(newScreen->name(), QStringLiteral("WL-3")); |
|
QCOMPARE(newScreen->geometry(), QRect(1920, 0, 1280, 720)); |
|
QVERIFY(!m_screenPool->screens().contains(newScreen)); |
|
|
|
QCOMPARE(m_screenPool->id(newScreen->name()), 2); |
|
QCOMPARE(m_screenPool->connector(2), QStringLiteral("WL-3")); |
|
} |
|
|
|
void ScreenPoolTest::testMoveOutOfRedundant() |
|
{ |
|
QSignalSpy addedSpy(m_screenPool, SIGNAL(screenAdded(QScreen *))); |
|
|
|
exec([=] { |
|
auto *out = output(2); |
|
auto *xdgOut = xdgOutput(out); |
|
out->m_data.mode.resolution = {1280, 2048}; |
|
xdgOut->sendLogicalSize(QSize(1280, 2048)); |
|
out->sendDone(); |
|
}); |
|
|
|
addedSpy.wait(); |
|
QCOMPARE(addedSpy.size(), 1); |
|
QScreen *newScreen = addedSpy.takeFirst().at(0).value<QScreen *>(); |
|
QCOMPARE(newScreen->name(), QStringLiteral("WL-3")); |
|
QCOMPARE(newScreen->geometry(), QRect(1920, 0, 1280, 2048)); |
|
QVERIFY(m_screenPool->screens().contains(newScreen)); |
|
} |
|
|
|
void ScreenPoolTest::testMoveInRedundant() |
|
{ |
|
QSignalSpy removedSpy(m_screenPool, SIGNAL(screenRemoved(QScreen *))); |
|
|
|
exec([=] { |
|
auto *out = output(2); |
|
auto *xdgOut = xdgOutput(out); |
|
out->m_data.mode.resolution = {1280, 720}; |
|
xdgOut->sendLogicalSize(QSize(1280, 720)); |
|
out->sendDone(); |
|
}); |
|
|
|
removedSpy.wait(); |
|
QCOMPARE(removedSpy.size(), 1); |
|
QScreen *oldScreen = removedSpy.takeFirst().at(0).value<QScreen *>(); |
|
QCOMPARE(oldScreen->name(), QStringLiteral("WL-3")); |
|
QCOMPARE(oldScreen->geometry(), QRect(1920, 0, 1280, 720)); |
|
QVERIFY(!m_screenPool->screens().contains(oldScreen)); |
|
} |
|
|
|
void ScreenPoolTest::testPrimarySwap() |
|
{ |
|
QSignalSpy primaryChangeSpy(m_screenPool, SIGNAL(primaryScreenChanged(QScreen *, QScreen *))); |
|
|
|
// Check ScreenPool mapping before switch |
|
QCOMPARE(m_screenPool->primaryConnector(), QStringLiteral("WL-1")); |
|
QCOMPARE(m_screenPool->primaryScreen()->name(), m_screenPool->primaryConnector()); |
|
QCOMPARE(m_screenPool->id(QStringLiteral("WL-1")), 0); |
|
QCOMPARE(m_screenPool->id(QStringLiteral("WL-2")), 1); |
|
|
|
// Set a primary screen |
|
exec([=] { |
|
primaryOutput()->setPrimaryOutputName("WL-2"); |
|
}); |
|
|
|
primaryChangeSpy.wait(); |
|
|
|
QCOMPARE(primaryChangeSpy.size(), 1); |
|
QScreen *oldPrimary = primaryChangeSpy[0].at(0).value<QScreen *>(); |
|
QScreen *newPrimary = primaryChangeSpy[0].at(1).value<QScreen *>(); |
|
QCOMPARE(oldPrimary->name(), QStringLiteral("WL-1")); |
|
QCOMPARE(oldPrimary->geometry(), QRect(0, 0, 1920, 1080)); |
|
QCOMPARE(newPrimary->name(), QStringLiteral("WL-2")); |
|
QCOMPARE(newPrimary->geometry(), QRect(1920, 0, 1920, 1080)); |
|
|
|
// Check ScreenPool mapping |
|
QCOMPARE(m_screenPool->primaryConnector(), QStringLiteral("WL-2")); |
|
QCOMPARE(m_screenPool->primaryConnector(), newPrimary->name()); |
|
QCOMPARE(m_screenPool->primaryScreen()->name(), m_screenPool->primaryConnector()); |
|
QCOMPARE(m_screenPool->id(newPrimary->name()), 0); |
|
QCOMPARE(m_screenPool->id(oldPrimary->name()), 1); |
|
} |
|
|
|
void ScreenPoolTest::testPrimarySwapToRedundant() |
|
{ |
|
QSignalSpy primaryChangeSpy(m_screenPool, SIGNAL(primaryScreenChanged(QScreen *, QScreen *))); |
|
|
|
// Set a primary screen |
|
exec([=] { |
|
primaryOutput()->setPrimaryOutputName("WL-3"); |
|
}); |
|
|
|
primaryChangeSpy.wait(250); |
|
// Nothing will happen, is like WL3 doesn't exist |
|
QCOMPARE(primaryChangeSpy.size(), 0); |
|
} |
|
|
|
void ScreenPoolTest::testMoveRedundantToMakePrimary() |
|
{ |
|
QSignalSpy addedSpy(m_screenPool, SIGNAL(screenAdded(QScreen *))); |
|
QSignalSpy primaryChangeSpy(m_screenPool, SIGNAL(primaryScreenChanged(QScreen *, QScreen *))); |
|
|
|
exec([=] { |
|
auto *out = output(2); |
|
auto *xdgOut = xdgOutput(out); |
|
out->m_data.mode.resolution = {1280, 2048}; |
|
xdgOut->sendLogicalSize(QSize(1280, 2048)); |
|
out->sendDone(); |
|
}); |
|
|
|
// Having multiple spies, when the wait of one will exit both signals will already have been emitted |
|
QTRY_COMPARE(addedSpy.size(), 1); |
|
QTRY_COMPARE(primaryChangeSpy.size(), 1); |
|
QScreen *newScreen = addedSpy.takeFirst().at(0).value<QScreen *>(); |
|
QCOMPARE(newScreen->name(), QStringLiteral("WL-3")); |
|
QCOMPARE(newScreen->geometry(), QRect(1920, 0, 1280, 2048)); |
|
QVERIFY(m_screenPool->screens().contains(newScreen)); |
|
|
|
// Test the new primary |
|
QScreen *oldPrimary = primaryChangeSpy[0].at(0).value<QScreen *>(); |
|
QScreen *newPrimary = primaryChangeSpy[0].at(1).value<QScreen *>(); |
|
QCOMPARE(oldPrimary->name(), QStringLiteral("WL-2")); |
|
QCOMPARE(oldPrimary->geometry(), QRect(1920, 0, 1920, 1080)); |
|
QCOMPARE(newPrimary->name(), QStringLiteral("WL-3")); |
|
QCOMPARE(newPrimary->geometry(), QRect(1920, 0, 1280, 2048)); |
|
|
|
// Check ScreenPool mapping |
|
QCOMPARE(m_screenPool->primaryConnector(), QStringLiteral("WL-3")); |
|
QCOMPARE(m_screenPool->primaryConnector(), newPrimary->name()); |
|
QCOMPARE(m_screenPool->primaryScreen()->name(), m_screenPool->primaryConnector()); |
|
QCOMPARE(m_screenPool->id(newPrimary->name()), 0); |
|
QCOMPARE(m_screenPool->id(oldPrimary->name()), 2); |
|
} |
|
|
|
void ScreenPoolTest::testMoveInRedundantToLosePrimary() |
|
{ |
|
QSignalSpy removedSpy(m_screenPool, SIGNAL(screenRemoved(QScreen *))); |
|
QSignalSpy primaryChangeSpy(m_screenPool, SIGNAL(primaryScreenChanged(QScreen *, QScreen *))); |
|
|
|
exec([=] { |
|
auto *out = output(2); |
|
auto *xdgOut = xdgOutput(out); |
|
xdgOut->sendLogicalSize(QSize(1280, 720)); |
|
out->m_data.mode.resolution = {1280, 720}; |
|
out->sendDone(); |
|
}); |
|
|
|
QTRY_COMPARE(primaryChangeSpy.size(), 1); |
|
// Test the new primary |
|
QScreen *oldPrimary = primaryChangeSpy[0].at(0).value<QScreen *>(); |
|
QScreen *newPrimary = primaryChangeSpy[0].at(1).value<QScreen *>(); |
|
QCOMPARE(oldPrimary->name(), QStringLiteral("WL-3")); |
|
QCOMPARE(oldPrimary->geometry(), QRect(1920, 0, 1280, 720)); |
|
QCOMPARE(newPrimary->name(), QStringLiteral("WL-2")); |
|
QCOMPARE(newPrimary->geometry(), QRect(1920, 0, 1920, 1080)); |
|
|
|
// Check ScreenPool mapping |
|
QCOMPARE(m_screenPool->primaryConnector(), QStringLiteral("WL-2")); |
|
QCOMPARE(m_screenPool->primaryConnector(), newPrimary->name()); |
|
QCOMPARE(m_screenPool->primaryScreen()->name(), m_screenPool->primaryConnector()); |
|
QCOMPARE(m_screenPool->id(newPrimary->name()), 0); |
|
QCOMPARE(m_screenPool->id(oldPrimary->name()), 2); |
|
|
|
QTRY_COMPARE(removedSpy.size(), 1); |
|
QScreen *oldScreen = removedSpy.takeFirst().at(0).value<QScreen *>(); |
|
QCOMPARE(oldScreen->name(), QStringLiteral("WL-3")); |
|
QCOMPARE(oldScreen->geometry(), QRect(1920, 0, 1280, 720)); |
|
QVERIFY(!m_screenPool->screens().contains(oldScreen)); |
|
} |
|
|
|
void ScreenPoolTest::testSecondScreenRemoval() |
|
{ |
|
QSignalSpy addedSpy(m_screenPool, SIGNAL(screenAdded(QScreen *))); |
|
QSignalSpy primaryChangeSpy(m_screenPool, SIGNAL(primaryScreenChanged(QScreen *, QScreen *))); |
|
QSignalSpy removedSpy(m_screenPool, SIGNAL(screenRemoved(QScreen *))); |
|
|
|
// Check ScreenPool mapping before switch |
|
QCOMPARE(m_screenPool->primaryConnector(), QStringLiteral("WL-2")); |
|
QCOMPARE(m_screenPool->primaryScreen()->name(), m_screenPool->primaryConnector()); |
|
QCOMPARE(m_screenPool->id(QStringLiteral("WL-2")), 0); |
|
QCOMPARE(m_screenPool->id(QStringLiteral("WL-1")), 1); |
|
|
|
// Remove an output |
|
exec([=] { |
|
remove(output(1)); |
|
}); |
|
|
|
// Removing the primary screen, will change a primaryChange signal beforehand |
|
QTRY_COMPARE(primaryChangeSpy.size(), 1); |
|
QCOMPARE(primaryChangeSpy.size(), 1); |
|
QScreen *newPrimary = primaryChangeSpy[0].at(1).value<QScreen *>(); |
|
QCOMPARE(newPrimary->name(), QStringLiteral("WL-3")); |
|
QCOMPARE(newPrimary->geometry(), QRect(1920, 0, 1280, 720)); |
|
|
|
// Check ScreenPool mapping |
|
QCOMPARE(m_screenPool->primaryConnector(), QStringLiteral("WL-3")); |
|
QCOMPARE(m_screenPool->primaryScreen()->name(), m_screenPool->primaryConnector()); |
|
QCOMPARE(m_screenPool->id(newPrimary->name()), 0); |
|
QCOMPARE(m_screenPool->id("WL-2"), 2); |
|
|
|
// NOTE: we can neither access the data of removedSpy nor oldPrimary because at this point will be dangling |
|
QTRY_COMPARE(removedSpy.size(), 1); |
|
QTRY_COMPARE(addedSpy.size(), 1); |
|
|
|
QCOMPARE(QGuiApplication::screens().size(), 2); |
|
QCOMPARE(m_screenPool->screens().size(), 2); |
|
QScreen *firstScreen = m_screenPool->screens().at(1); |
|
QCOMPARE(firstScreen, newPrimary); |
|
QCOMPARE(m_screenPool->primaryScreen(), newPrimary); |
|
|
|
// We'll get an added signal for the screen WL-3 that was previously redundant to WL-2 |
|
QScreen *newScreen = addedSpy[0].at(0).value<QScreen *>(); |
|
QCOMPARE(newScreen->name(), QStringLiteral("WL-3")); |
|
QCOMPARE(newScreen->geometry(), QRect(1920, 0, 1280, 720)); |
|
QCOMPARE(m_screenPool->screens().at(1), newScreen); |
|
} |
|
|
|
void ScreenPoolTest::testThirdScreenRemoval() |
|
{ |
|
QSignalSpy removedSpy(m_screenPool, SIGNAL(screenRemoved(QScreen *))); |
|
|
|
// Remove an output |
|
exec([=] { |
|
// NOTE: Assume the server will always do the right thing to change the primary screen before deleting one |
|
primaryOutput()->setPrimaryOutputName("WL-1"); |
|
|
|
remove(output(1)); |
|
}); |
|
|
|
// NOTE: we can neither access the data of removedSpy nor oldPrimary because at this point will be dangling |
|
removedSpy.wait(); |
|
QCOMPARE(QGuiApplication::screens().size(), 1); |
|
QCOMPARE(m_screenPool->screens().size(), 1); |
|
QScreen *lastScreen = m_screenPool->screens().first(); |
|
QCOMPARE(lastScreen->name(), QStringLiteral("WL-1")); |
|
QCOMPARE(lastScreen->geometry(), QRect(0, 0, 1920, 1080)); |
|
QCOMPARE(m_screenPool->screens().first(), lastScreen); |
|
// This shouldn't have changed after removing a non primary screen |
|
QCOMPARE(m_screenPool->primaryConnector(), QStringLiteral("WL-1")); |
|
} |
|
|
|
void ScreenPoolTest::testLastScreenRemoval() |
|
{ |
|
QSignalSpy removedSpy(m_screenPool, SIGNAL(screenRemoved(QScreen *))); |
|
|
|
// Remove an output |
|
exec([=] { |
|
remove(output(0)); |
|
}); |
|
|
|
// NOTE: we can neither access the data of removedSpy nor oldPrimary because at this point will be dangling |
|
removedSpy.wait(); |
|
QCOMPARE(QGuiApplication::screens().size(), 1); |
|
QCOMPARE(m_screenPool->screens().size(), 0); |
|
QScreen *fakeScreen = QGuiApplication::screens().first(); |
|
QCOMPARE(fakeScreen->name(), QString()); |
|
QCOMPARE(fakeScreen->geometry(), QRect(0, 0, 0, 0)); |
|
} |
|
|
|
void ScreenPoolTest::testFakeToRealScreen() |
|
{ |
|
QSignalSpy addedSpy(m_screenPool, SIGNAL(screenAdded(QScreen *))); |
|
|
|
// Add a new output |
|
exec([=] { |
|
OutputData data; |
|
data.mode.resolution = {1920, 1080}; |
|
data.position = {0, 0}; |
|
data.physicalSize = data.mode.physicalSizeForDpi(96); |
|
auto *out = add<Output>(data); |
|
auto *xdgOut = xdgOutput(out); |
|
xdgOut->m_name = QStringLiteral("WL-1"); |
|
}); |
|
|
|
addedSpy.wait(); |
|
QCOMPARE(QGuiApplication::screens().size(), 1); |
|
QCOMPARE(m_screenPool->screens().size(), 1); |
|
QCOMPARE(addedSpy.size(), 1); |
|
|
|
QScreen *newScreen = addedSpy.takeFirst().at(0).value<QScreen *>(); |
|
QCOMPARE(newScreen->name(), QStringLiteral("WL-1")); |
|
QCOMPARE(newScreen->geometry(), QRect(0, 0, 1920, 1080)); |
|
|
|
QCOMPARE(m_screenPool->id(newScreen->name()), 0); |
|
} |
|
|
|
QCOMPOSITOR_TEST_MAIN(ScreenPoolTest) |
|
|
|
#include "screenpooltest.moc"
|
|
|