[autotest] Add test for transient placement positioning
Test creates windows and transients for it and verifies the position. For decorated windows the position is currently broken as it does not consider the adjusted client position.
This commit is contained in:
parent
d678ffc8c5
commit
f2873dcd36
2 changed files with 316 additions and 0 deletions
|
@ -116,3 +116,12 @@ add_executable(testDontCrashCancelAnimation ${testDontCrashCancelAnimation_SRCS}
|
|||
target_link_libraries( testDontCrashCancelAnimation kwin Qt5::Test)
|
||||
add_test(kwin-testDontCrashCancelAnimation testDontCrashCancelAnimation)
|
||||
ecm_mark_as_test(testDontCrashCancelAnimation)
|
||||
|
||||
########################################################
|
||||
# Transient Placement Test
|
||||
########################################################
|
||||
set( testTransientPlacmenet_SRCS transient_placement.cpp kwin_wayland_test.cpp )
|
||||
add_executable(testTransientPlacmenet ${testTransientPlacmenet_SRCS})
|
||||
target_link_libraries( testTransientPlacmenet kwin Qt5::Test)
|
||||
add_test(kwin-testTransientPlacmenet testTransientPlacmenet)
|
||||
ecm_mark_as_test(testTransientPlacmenet)
|
||||
|
|
307
autotests/wayland/transient_placement.cpp
Normal file
307
autotests/wayland/transient_placement.cpp
Normal file
|
@ -0,0 +1,307 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2016 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#include "kwin_wayland_test.h"
|
||||
#include "abstract_backend.h"
|
||||
#include "abstract_client.h"
|
||||
#include "cursor.h"
|
||||
#include "screenedge.h"
|
||||
#include "screens.h"
|
||||
#include "wayland_server.h"
|
||||
#include "workspace.h"
|
||||
#include "shell_client.h"
|
||||
#include <kwineffects.h>
|
||||
|
||||
#include <KWayland/Client/connection_thread.h>
|
||||
#include <KWayland/Client/compositor.h>
|
||||
#include <KWayland/Client/event_queue.h>
|
||||
#include <KWayland/Client/keyboard.h>
|
||||
#include <KWayland/Client/registry.h>
|
||||
#include <KWayland/Client/pointer.h>
|
||||
#include <KWayland/Client/shell.h>
|
||||
#include <KWayland/Client/seat.h>
|
||||
#include <KWayland/Client/server_decoration.h>
|
||||
#include <KWayland/Client/shm_pool.h>
|
||||
#include <KWayland/Client/surface.h>
|
||||
#include <KWayland/Client/touch.h>
|
||||
#include <KWayland/Server/seat_interface.h>
|
||||
#include <KWayland/Server/surface_interface.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
static const QString s_socketName = QStringLiteral("wayland_test_kwin_transient_placement-0");
|
||||
|
||||
class TransientPlacementTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void init();
|
||||
void cleanup();
|
||||
void testSimplePosition_data();
|
||||
void testSimplePosition();
|
||||
void testDecorationPosition_data();
|
||||
void testDecorationPosition();
|
||||
|
||||
private:
|
||||
AbstractClient *showWindow(const QSize &size, bool decorated = false, KWayland::Client::Surface *parent = nullptr, const QPoint &offset = QPoint());
|
||||
KWayland::Client::Surface *surfaceForClient(AbstractClient *c) const;
|
||||
KWayland::Client::ConnectionThread *m_connection = nullptr;
|
||||
KWayland::Client::Compositor *m_compositor = nullptr;
|
||||
KWayland::Client::ServerSideDecorationManager *m_deco = nullptr;
|
||||
KWayland::Client::Seat *m_seat = nullptr;
|
||||
KWayland::Client::ShmPool *m_shm = nullptr;
|
||||
KWayland::Client::Shell *m_shell = nullptr;
|
||||
KWayland::Client::EventQueue *m_queue = nullptr;
|
||||
QThread *m_thread = nullptr;
|
||||
};
|
||||
|
||||
void TransientPlacementTest::initTestCase()
|
||||
{
|
||||
qRegisterMetaType<KWin::ShellClient*>();
|
||||
qRegisterMetaType<KWin::AbstractClient*>();
|
||||
QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated);
|
||||
QVERIFY(workspaceCreatedSpy.isValid());
|
||||
waylandServer()->backend()->setInitialWindowSize(QSize(1280, 1024));
|
||||
QMetaObject::invokeMethod(waylandServer()->backend(), "setOutputCount", Qt::DirectConnection, Q_ARG(int, 2));
|
||||
waylandServer()->init(s_socketName.toLocal8Bit());
|
||||
|
||||
kwinApp()->start();
|
||||
QVERIFY(workspaceCreatedSpy.wait());
|
||||
QCOMPARE(screens()->count(), 2);
|
||||
QCOMPARE(screens()->geometry(0), QRect(0, 0, 1280, 1024));
|
||||
QCOMPARE(screens()->geometry(1), QRect(1280, 0, 1280, 1024));
|
||||
setenv("QT_QPA_PLATFORM", "wayland", true);
|
||||
waylandServer()->initWorkspace();
|
||||
}
|
||||
|
||||
void TransientPlacementTest::init()
|
||||
{
|
||||
using namespace KWayland::Client;
|
||||
// setup connection
|
||||
m_connection = new ConnectionThread;
|
||||
QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected);
|
||||
QVERIFY(connectedSpy.isValid());
|
||||
m_connection->setSocketName(s_socketName);
|
||||
|
||||
m_thread = new QThread(this);
|
||||
m_connection->moveToThread(m_thread);
|
||||
m_thread->start();
|
||||
|
||||
m_connection->initConnection();
|
||||
QVERIFY(connectedSpy.wait());
|
||||
|
||||
m_queue = new EventQueue(this);
|
||||
QVERIFY(!m_queue->isValid());
|
||||
m_queue->setup(m_connection);
|
||||
QVERIFY(m_queue->isValid());
|
||||
|
||||
Registry registry;
|
||||
registry.setEventQueue(m_queue);
|
||||
QSignalSpy compositorSpy(®istry, &Registry::compositorAnnounced);
|
||||
QSignalSpy shmSpy(®istry, &Registry::shmAnnounced);
|
||||
QSignalSpy shellSpy(®istry, &Registry::shellAnnounced);
|
||||
QSignalSpy seatSpy(®istry, &Registry::seatAnnounced);
|
||||
QSignalSpy allAnnounced(®istry, &Registry::interfacesAnnounced);
|
||||
QVERIFY(allAnnounced.isValid());
|
||||
QVERIFY(shmSpy.isValid());
|
||||
QVERIFY(shellSpy.isValid());
|
||||
QVERIFY(compositorSpy.isValid());
|
||||
QVERIFY(seatSpy.isValid());
|
||||
registry.create(m_connection->display());
|
||||
QVERIFY(registry.isValid());
|
||||
registry.setup();
|
||||
QVERIFY(allAnnounced.wait());
|
||||
QVERIFY(!compositorSpy.isEmpty());
|
||||
QVERIFY(!shmSpy.isEmpty());
|
||||
QVERIFY(!shellSpy.isEmpty());
|
||||
QVERIFY(!seatSpy.isEmpty());
|
||||
|
||||
m_compositor = registry.createCompositor(compositorSpy.first().first().value<quint32>(), compositorSpy.first().last().value<quint32>(), this);
|
||||
QVERIFY(m_compositor->isValid());
|
||||
m_shm = registry.createShmPool(shmSpy.first().first().value<quint32>(), shmSpy.first().last().value<quint32>(), this);
|
||||
QVERIFY(m_shm->isValid());
|
||||
m_shell = registry.createShell(shellSpy.first().first().value<quint32>(), shellSpy.first().last().value<quint32>(), this);
|
||||
QVERIFY(m_shell->isValid());
|
||||
m_seat = registry.createSeat(seatSpy.first().first().value<quint32>(), seatSpy.first().last().value<quint32>(), this);
|
||||
QVERIFY(m_seat->isValid());
|
||||
QSignalSpy hasPointerSpy(m_seat, &Seat::hasPointerChanged);
|
||||
QVERIFY(hasPointerSpy.isValid());
|
||||
QVERIFY(hasPointerSpy.wait());
|
||||
|
||||
m_deco = registry.createServerSideDecorationManager(registry.interface(Registry::Interface::ServerSideDecorationManager).name, registry.interface(Registry::Interface::ServerSideDecorationManager).version, this);
|
||||
QVERIFY(m_deco->isValid());
|
||||
|
||||
screens()->setCurrent(0);
|
||||
Cursor::setPos(QPoint(640, 512));
|
||||
}
|
||||
|
||||
void TransientPlacementTest::cleanup()
|
||||
{
|
||||
delete m_deco;
|
||||
m_deco = nullptr;
|
||||
delete m_compositor;
|
||||
m_compositor = nullptr;
|
||||
delete m_seat;
|
||||
m_seat = nullptr;
|
||||
delete m_shm;
|
||||
m_shm = nullptr;
|
||||
delete m_shell;
|
||||
m_shell = nullptr;
|
||||
delete m_queue;
|
||||
m_queue = nullptr;
|
||||
if (m_thread) {
|
||||
m_connection->deleteLater();
|
||||
m_thread->quit();
|
||||
m_thread->wait();
|
||||
delete m_thread;
|
||||
m_thread = nullptr;
|
||||
m_connection = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
AbstractClient *TransientPlacementTest::showWindow(const QSize &size, bool decorated, KWayland::Client::Surface *parent, const QPoint &offset)
|
||||
{
|
||||
using namespace KWayland::Client;
|
||||
#define VERIFY(statement) \
|
||||
if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__))\
|
||||
return nullptr;
|
||||
#define COMPARE(actual, expected) \
|
||||
if (!QTest::qCompare(actual, expected, #actual, #expected, __FILE__, __LINE__))\
|
||||
return nullptr;
|
||||
QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded);
|
||||
VERIFY(clientAddedSpy.isValid());
|
||||
|
||||
Surface *surface = m_compositor->createSurface(m_compositor);
|
||||
VERIFY(surface);
|
||||
ShellSurface *shellSurface = m_shell->createSurface(surface, surface);
|
||||
VERIFY(shellSurface);
|
||||
if (parent) {
|
||||
shellSurface->setTransient(parent, offset);
|
||||
}
|
||||
if (decorated) {
|
||||
auto deco = m_deco->create(surface, surface);
|
||||
QSignalSpy decoSpy(deco, &ServerSideDecoration::modeChanged);
|
||||
VERIFY(decoSpy.isValid());
|
||||
VERIFY(decoSpy.wait());
|
||||
deco->requestMode(ServerSideDecoration::Mode::Server);
|
||||
VERIFY(decoSpy.wait());
|
||||
COMPARE(deco->mode(), ServerSideDecoration::Mode::Server);
|
||||
}
|
||||
// let's render
|
||||
QImage img(size, QImage::Format_ARGB32);
|
||||
img.fill(Qt::blue);
|
||||
surface->attachBuffer(m_shm->createBuffer(img));
|
||||
surface->damage(QRect(QPoint(0, 0), size));
|
||||
surface->commit(Surface::CommitFlag::None);
|
||||
|
||||
m_connection->flush();
|
||||
VERIFY(clientAddedSpy.wait());
|
||||
AbstractClient *c = workspace()->activeClient();
|
||||
VERIFY(c);
|
||||
COMPARE(clientAddedSpy.first().first().value<ShellClient*>(), c);
|
||||
|
||||
#undef VERIFY
|
||||
#undef COMPARE
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
KWayland::Client::Surface *TransientPlacementTest::surfaceForClient(AbstractClient *c) const
|
||||
{
|
||||
const auto &surfaces = KWayland::Client::Surface::all();
|
||||
auto it = std::find_if(surfaces.begin(), surfaces.end(), [c] (KWayland::Client::Surface *s) { return s->id() == c->surface()->id(); });
|
||||
if (it != surfaces.end()) {
|
||||
return *it;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void TransientPlacementTest::testSimplePosition_data()
|
||||
{
|
||||
QTest::addColumn<QSize>("parentSize");
|
||||
QTest::addColumn<QPoint>("parentPosition");
|
||||
QTest::addColumn<QSize>("transientSize");
|
||||
QTest::addColumn<QPoint>("transientOffset");
|
||||
QTest::addColumn<QRect>("expectedGeometry");
|
||||
|
||||
QTest::newRow("0/0") << QSize(640, 512) << QPoint(0, 0) << QSize(10, 100) << QPoint(0, 0) << QRect(0, 0, 10, 100);
|
||||
QTest::newRow("bottomRight") << QSize(640, 512) << QPoint(0, 0) << QSize(10, 100) << QPoint(639, 511) << QRect(639, 511, 10, 100);
|
||||
QTest::newRow("offset") << QSize(640, 512) << QPoint(200, 300) << QSize(100, 10) << QPoint(320, 256) << QRect(520, 556, 100, 10);
|
||||
}
|
||||
|
||||
void TransientPlacementTest::testSimplePosition()
|
||||
{
|
||||
// this test verifies that the position of a transient window is taken from the passed position
|
||||
// there are no further constraints like window too large to fit screen, cascading transients, etc
|
||||
QFETCH(QSize, parentSize);
|
||||
AbstractClient *parent = showWindow(parentSize);
|
||||
QVERIFY(parent->clientPos().isNull());
|
||||
QVERIFY(!parent->isDecorated());
|
||||
QFETCH(QPoint, parentPosition);
|
||||
parent->move(parentPosition);
|
||||
QFETCH(QSize, transientSize);
|
||||
QFETCH(QPoint, transientOffset);
|
||||
AbstractClient *transient = showWindow(transientSize, false, surfaceForClient(parent), transientOffset);
|
||||
QVERIFY(transient);
|
||||
QVERIFY(!transient->isDecorated());
|
||||
QVERIFY(transient->hasTransientPlacementHint());
|
||||
QTEST(transient->geometry(), "expectedGeometry");
|
||||
}
|
||||
|
||||
void TransientPlacementTest::testDecorationPosition_data()
|
||||
{
|
||||
QTest::addColumn<QSize>("parentSize");
|
||||
QTest::addColumn<QPoint>("parentPosition");
|
||||
QTest::addColumn<QSize>("transientSize");
|
||||
QTest::addColumn<QPoint>("transientOffset");
|
||||
QTest::addColumn<QRect>("expectedGeometry");
|
||||
|
||||
QTest::newRow("0/0") << QSize(640, 512) << QPoint(0, 0) << QSize(10, 100) << QPoint(0, 0) << QRect(0, 0, 10, 100);
|
||||
QTest::newRow("bottomRight") << QSize(640, 512) << QPoint(0, 0) << QSize(10, 100) << QPoint(639, 511) << QRect(639, 511, 10, 100);
|
||||
QTest::newRow("offset") << QSize(640, 512) << QPoint(200, 300) << QSize(100, 10) << QPoint(320, 256) << QRect(520, 556, 100, 10);
|
||||
}
|
||||
|
||||
void TransientPlacementTest::testDecorationPosition()
|
||||
{
|
||||
// this test verifies that a transient window is correctly placed if the parent window has a
|
||||
// server side decoration
|
||||
QFETCH(QSize, parentSize);
|
||||
AbstractClient *parent = showWindow(parentSize, true);
|
||||
QVERIFY(!parent->clientPos().isNull());
|
||||
QVERIFY(parent->isDecorated());
|
||||
QFETCH(QPoint, parentPosition);
|
||||
parent->move(parentPosition);
|
||||
QFETCH(QSize, transientSize);
|
||||
QFETCH(QPoint, transientOffset);
|
||||
AbstractClient *transient = showWindow(transientSize, false, surfaceForClient(parent), transientOffset);
|
||||
QVERIFY(transient);
|
||||
QVERIFY(!transient->isDecorated());
|
||||
QVERIFY(transient->hasTransientPlacementHint());
|
||||
QFETCH(QRect, expectedGeometry);
|
||||
expectedGeometry.translate(parent->clientPos());
|
||||
QEXPECT_FAIL("", "Fix me", Continue);
|
||||
QCOMPARE(transient->geometry(), expectedGeometry);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
WAYLANDTEST_MAIN(KWin::TransientPlacementTest)
|
||||
#include "transient_placement.moc"
|
Loading…
Reference in a new issue