From 2cc758045e322873ad58f675a70c8d8366b5c318 Mon Sep 17 00:00:00 2001 From: Paul Olav Tvete Date: Fri, 5 Aug 2016 11:53:43 +0200 Subject: Fix autotest directory structure The client and server parts are separate modules, so they need separate top-level directories under tests/auto. This also makes it easier to add new tests later. Change-Id: I393341b6f4e8fc3afa480653f3482192e002e425 Reviewed-by: Giulio Camuffo --- .../auto/compositor/compositor/tst_compositor.cpp | 395 +++++++++++++++++++++ 1 file changed, 395 insertions(+) create mode 100644 tests/auto/compositor/compositor/tst_compositor.cpp (limited to 'tests/auto/compositor/compositor/tst_compositor.cpp') diff --git a/tests/auto/compositor/compositor/tst_compositor.cpp b/tests/auto/compositor/compositor/tst_compositor.cpp new file mode 100644 index 000000000..c30ebc02b --- /dev/null +++ b/tests/auto/compositor/compositor/tst_compositor.cpp @@ -0,0 +1,395 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mockclient.h" +#include "testcompositor.h" +#include "testkeyboardgrabber.h" + +#include "QtCompositor/private/qwlkeyboard_p.h" +#include "QtCompositor/private/qwlinputdevice_p.h" +#include "QtCompositor/private/qwlcompositor_p.h" +#include "testinputdevice.h" + +#include "qwaylandbufferref.h" + +#include + +#include + +class tst_WaylandCompositor : public QObject +{ + Q_OBJECT + +public: + tst_WaylandCompositor() { + setenv("XDG_RUNTIME_DIR", ".", 1); + } + +private slots: + void inputDeviceCapabilities(); + void keyboardGrab(); + void inputDeviceCreation(); + void inputDeviceKeyboardFocus(); + void singleClient(); + void multipleClients(); + void geometry(); + void mapSurface(); + void frameCallback(); +}; + +void tst_WaylandCompositor::singleClient() +{ + TestCompositor compositor; + + MockClient client; + + wl_surface *sa = client.createSurface(); + QTRY_COMPARE(compositor.surfaces.size(), 1); + + wl_surface *sb = client.createSurface(); + QTRY_COMPARE(compositor.surfaces.size(), 2); + + QWaylandClient *ca = compositor.surfaces.at(0)->client(); + QWaylandClient *cb = compositor.surfaces.at(1)->client(); + + QCOMPARE(ca, cb); + QVERIFY(ca != 0); + + QList surfaces = compositor.surfacesForClient(ca); + QCOMPARE(surfaces.size(), 2); + QVERIFY((surfaces.at(0) == compositor.surfaces.at(0) && surfaces.at(1) == compositor.surfaces.at(1)) + || (surfaces.at(0) == compositor.surfaces.at(1) && surfaces.at(1) == compositor.surfaces.at(0))); + + wl_surface_destroy(sa); + QTRY_COMPARE(compositor.surfaces.size(), 1); + + wl_surface_destroy(sb); + QTRY_COMPARE(compositor.surfaces.size(), 0); +} + +void tst_WaylandCompositor::multipleClients() +{ + TestCompositor compositor; + + MockClient a; + MockClient b; + MockClient c; + + wl_surface *sa = a.createSurface(); + wl_surface *sb = b.createSurface(); + wl_surface *sc = c.createSurface(); + + QTRY_COMPARE(compositor.surfaces.size(), 3); + + QWaylandClient *ca = compositor.surfaces.at(0)->client(); + QWaylandClient *cb = compositor.surfaces.at(1)->client(); + QWaylandClient *cc = compositor.surfaces.at(2)->client(); + + QVERIFY(ca != cb); + QVERIFY(ca != cc); + QVERIFY(cb != cc); + QVERIFY(ca != 0); + + QCOMPARE(compositor.surfacesForClient(ca).size(), 1); + QCOMPARE(compositor.surfacesForClient(ca).at(0), compositor.surfaces.at(0)); + + QCOMPARE(compositor.surfacesForClient(cb).size(), 1); + QCOMPARE(compositor.surfacesForClient(cb).at(0), compositor.surfaces.at(1)); + + QCOMPARE(compositor.surfacesForClient(cc).size(), 1); + QCOMPARE(compositor.surfacesForClient(cc).at(0), compositor.surfaces.at(2)); + + wl_surface_destroy(sa); + wl_surface_destroy(sb); + wl_surface_destroy(sc); + + QTRY_COMPARE(compositor.surfaces.size(), 0); +} + +void tst_WaylandCompositor::keyboardGrab() +{ + TestCompositor compositor((QWaylandCompositor::ExtensionFlag)0); + MockClient mc; + + mc.createSurface(); + // This is needed for timing purposes, otherwise the query for the + // compositor surfaces will return null + QTRY_COMPARE(compositor.surfaces.size(), 1); + + // Set the focused surface so that key event will flow through + QWaylandSurface *waylandSurface = compositor.surfaces.at(0); + QWaylandInputDevice* inputDevice = compositor.defaultInputDevice(); + inputDevice->handle()->keyboardDevice()->setFocus(waylandSurface->handle()); + + TestKeyboardGrabber grab; + QSignalSpy grabFocusSpy(&grab, SIGNAL(focusedCalled())); + QSignalSpy grabKeySpy(&grab, SIGNAL(keyCalled())); + QSignalSpy grabModifierSpy(&grab, SIGNAL(modifiersCalled())); + + QtWayland::Keyboard *keyboard = inputDevice->handle()->keyboardDevice(); + keyboard->startGrab(&grab); + + QTRY_COMPARE(grabFocusSpy.count(), 1); + QCOMPARE(grab.m_keyboard, inputDevice->handle()->keyboardDevice()); + + QKeyEvent ke(QEvent::KeyPress, Qt::Key_A, Qt::NoModifier, 30, 0, 0); + QKeyEvent ke1(QEvent::KeyRelease, Qt::Key_A, Qt::NoModifier, 30, 0, 0); + inputDevice->sendFullKeyEvent(&ke); + inputDevice->sendFullKeyEvent(&ke1); + QTRY_COMPARE(grabKeySpy.count(), 2); + + QKeyEvent ke2(QEvent::KeyPress, Qt::Key_Shift, Qt::NoModifier, 50, 0, 0); + QKeyEvent ke3(QEvent::KeyRelease, Qt::Key_Shift, Qt::NoModifier, 50, 0, 0); + inputDevice->sendFullKeyEvent(&ke2); + inputDevice->sendFullKeyEvent(&ke3); + QTRY_COMPARE(grabModifierSpy.count(), 2); + // Modifiers are also keys + QTRY_COMPARE(grabKeySpy.count(), 4); + + // Stop grabbing + keyboard->endGrab(); + inputDevice->sendFullKeyEvent(&ke); + inputDevice->sendFullKeyEvent(&ke1); + QTRY_COMPARE(grabKeySpy.count(), 4); +} + +void tst_WaylandCompositor::geometry() +{ + TestCompositor compositor; + + QRect geometry(0, 0, 4096, 3072); + compositor.setOutputGeometry(geometry); + + MockClient client; + + QTRY_COMPARE(client.geometry, geometry); +} + +void tst_WaylandCompositor::mapSurface() +{ + TestCompositor compositor; + + MockClient client; + + wl_surface *surface = client.createSurface(); + QTRY_COMPARE(compositor.surfaces.size(), 1); + + QWaylandSurface *waylandSurface = compositor.surfaces.at(0); + + QSignalSpy mappedSpy(waylandSurface, SIGNAL(mapped())); + + QCOMPARE(waylandSurface->size(), QSize()); + QCOMPARE(waylandSurface->type(), QWaylandSurface::Invalid); + + QSize size(256, 256); + ShmBuffer buffer(size, client.shm); + + // we need to create a shell surface here or the surface won't be mapped + client.createShellSurface(surface); + wl_surface_attach(surface, buffer.handle, 0, 0); + wl_surface_damage(surface, 0, 0, size.width(), size.height()); + wl_surface_commit(surface); + + QTRY_COMPARE(waylandSurface->size(), size); + QTRY_COMPARE(waylandSurface->type(), QWaylandSurface::Shm); + QTRY_COMPARE(mappedSpy.count(), 1); + + wl_surface_destroy(surface); +} + +static void frameCallbackFunc(void *data, wl_callback *callback, uint32_t) +{ + ++*static_cast(data); + wl_callback_destroy(callback); +} + +static void registerFrameCallback(wl_surface *surface, int *counter) +{ + static const wl_callback_listener frameCallbackListener = { + frameCallbackFunc + }; + + wl_callback_add_listener(wl_surface_frame(surface), &frameCallbackListener, counter); +} + +void tst_WaylandCompositor::frameCallback() +{ + class BufferAttacher : public QWaylandBufferAttacher + { + public: + void attach(const QWaylandBufferRef &ref) Q_DECL_OVERRIDE + { + bufferRef = ref; + } + void unmap() Q_DECL_OVERRIDE + { + } + + QImage image() const + { + if (!bufferRef || !bufferRef.isShm()) + return QImage(); + return bufferRef.image(); + } + + QWaylandBufferRef bufferRef; + }; + + TestCompositor compositor; + + MockClient client; + + wl_surface *surface = client.createSurface(); + + int frameCounter = 0; + + QTRY_COMPARE(compositor.surfaces.size(), 1); + QWaylandSurface *waylandSurface = compositor.surfaces.at(0); + BufferAttacher attacher; + waylandSurface->setBufferAttacher(&attacher); + QSignalSpy damagedSpy(waylandSurface, SIGNAL(damaged(const QRegion &))); + + for (int i = 0; i < 10; ++i) { + QSize size(i * 8 + 2, i * 8 + 2); + ShmBuffer buffer(size, client.shm); + + // attach a new buffer every frame, else the damage signal won't be fired + wl_surface_attach(surface, buffer.handle, 0, 0); + registerFrameCallback(surface, &frameCounter); + wl_surface_damage(surface, 0, 0, size.width(), size.height()); + wl_surface_commit(surface); + + QTRY_COMPARE(waylandSurface->type(), QWaylandSurface::Shm); + QTRY_COMPARE(damagedSpy.count(), i + 1); + + QCOMPARE(static_cast(waylandSurface->bufferAttacher())->image(), buffer.image); + compositor.frameStarted(); + compositor.sendFrameCallbacks(QList() << waylandSurface); + + QTRY_COMPARE(frameCounter, i + 1); + } + + wl_surface_destroy(surface); +} + +void tst_WaylandCompositor::inputDeviceCapabilities() +{ + TestCompositor compositor; + QtWayland::InputDevice dev(NULL, compositor.handle(), QWaylandInputDevice::Pointer); + + QTRY_VERIFY(dev.pointerDevice()); + QTRY_VERIFY(!dev.keyboardDevice()); + QTRY_VERIFY(!dev.touchDevice()); + + dev.setCapabilities(QWaylandInputDevice::Keyboard | QWaylandInputDevice::Touch); + QTRY_VERIFY(!dev.pointerDevice()); + QTRY_VERIFY(dev.keyboardDevice()); + QTRY_VERIFY(dev.touchDevice()); + + // Test that existing devices do not change when another is removed + QtWayland::Keyboard *k = dev.keyboardDevice(); + dev.setCapabilities(QWaylandInputDevice::Keyboard); + QTRY_COMPARE(k, dev.keyboardDevice()); +} + +void tst_WaylandCompositor::inputDeviceCreation() +{ + TestCompositor compositor; + TestInputDevice dev1(&compositor, QWaylandInputDevice::Pointer | QWaylandInputDevice::Keyboard); + TestInputDevice dev2(&compositor, QWaylandInputDevice::Pointer | QWaylandInputDevice::Keyboard); + + compositor.handle()->registerInputDevice(&dev1); + compositor.handle()->registerInputDevice(&dev2); + + // The compositor will create the default input device + QTRY_COMPARE(compositor.handle()->inputDevices().count(), 3); + // Test the order + QTRY_COMPARE(compositor.handle()->inputDevices().at(0), &dev2); + QTRY_COMPARE(compositor.handle()->inputDevices().at(1), &dev1); + QTRY_COMPARE(compositor.handle()->inputDevices().at(2), compositor.defaultInputDevice()); + + QList allEvents; + allEvents += dev1.createMouseEvents(2); + allEvents += dev2.createMouseEvents(5); + foreach (QMouseEvent *me, allEvents) { + compositor.inputDeviceFor(me); + } + + // The first input device will only get called exatly the number of times it has created + // the events + QTRY_COMPARE(dev1.queryCount(), 2); + // The second will get called the total number of times as it sits as the first item in + // the registered input devices list + QTRY_COMPARE(dev2.queryCount(), 7); +} + +void tst_WaylandCompositor::inputDeviceKeyboardFocus() +{ + TestCompositor compositor; + + + TestInputDevice dev1(&compositor, QWaylandInputDevice::Keyboard); + TestInputDevice dev2(&compositor, QWaylandInputDevice::Keyboard); + + compositor.handle()->registerInputDevice(&dev1); + compositor.handle()->registerInputDevice(&dev2); + + // Create client after all the input devices have been set up as the mock client + // does not dynamically listen to new seats + MockClient client; + wl_surface *surface = client.createSurface(); + QTRY_COMPARE(compositor.surfaces.size(), 1); + + QWaylandSurface *waylandSurface = compositor.surfaces.at(0); + QList devices = compositor.handle()->inputDevices(); + foreach (QWaylandInputDevice *dev, devices) { + dev->setKeyboardFocus(waylandSurface); + } + QTRY_COMPARE(compositor.defaultInputDevice()->keyboardFocus(), waylandSurface); + QTRY_COMPARE(dev1.keyboardFocus(), waylandSurface); + QTRY_COMPARE(dev2.keyboardFocus(), waylandSurface); + + wl_surface_destroy(surface); + QTRY_VERIFY(compositor.surfaces.size() == 0); + // This will normally be called for example in the quick compositor + // but here call it manually to get rid of the surface and have it reset + // the focus + compositor.handle()->cleanupGraphicsResources(); + + QTRY_VERIFY(!compositor.defaultInputDevice()->keyboardFocus()); + QTRY_VERIFY(!dev1.keyboardFocus()); + QTRY_VERIFY(!dev2.keyboardFocus()); +} + +#include +QTEST_MAIN(tst_WaylandCompositor); -- cgit v1.2.3