summaryrefslogtreecommitdiffstats
path: root/tests/auto/client/shared/mockcompositor.h
diff options
context:
space:
mode:
authorJohan Klokkhammer Helsing <johan.helsing@qt.io>2018-10-24 08:46:06 +0200
committerJohan Helsing <johan.helsing@qt.io>2018-12-11 15:46:49 +0000
commitddef100d9fa7014b21280b1380e6fbcef80277b3 (patch)
treecd0e674010a585fd6bc28cf0c26523392d8fbb8b /tests/auto/client/shared/mockcompositor.h
parent32b6f5f41349d109f872caf42a44352744bd48e2 (diff)
Create a new type of mock compositor for client tests
There are a number of issues with the current client testing: - Adding new compositor functionality is cumbersome (need to add compositor send method, command, implementation, not to mention creating new wrapper objects. - Customizing available globals and their versions is not possible and would be hard to implement. I.e. how to test that functionality works with old and new versions of an interface? Handle globals being destroyed. We did this with wl_output, but it was painfully cumbersome. - Hard to verify that the compositor state is clean between tests. It is currently done in some tests, but requires boiler plate code which needs to be added and maintained for each test. - In general lots of boiler-plate for new tests. (We have to have separate tests as long as Qt has global/static state. I.e. if one shell extension has been initialized, we can't deinitialize and initialize another one, so tests have to be separate.) - Dispatching server events tied to the client event loop sometimes makes it hard to write tests without deadlocks. - Abstraction, encapsulation and automatic behavior that can't be disabled makes it hard to test low-level functionality like surface exposure. So, in an attempt to mitigate these issues, I wrote a new testing framework. - Compositor dispatch is running continuously in it's own thread, access to compositor state is guarded by a mutex on the compositor, locking this will make dispatching stop, so the test can safely access internals. Although a bit cumbersome at first this makes it much easier to directly use server protocol commands from the test itself, i.e. no need to create commands for every single thing we want to test. - The CoreCompositor::exec template method can accept a lambda that will be run with dispatching stopped. It can also return a value, conveniently letting us safely extract or modify compositor state from tests. - This framework also takes full advantage of the qtwaylandscanner, using wrapper classes for everything, reducing boiler plate considerably. - The compositor parts are designed to do as little as possible automatically, but still provide easy ways to enable common functionality, like releasing buffers automatically, configuring shell surfaces etc. - Compositor globals are pluggable, use add<GlobalClass>() and remove<GlobalClass>() to add new global interfaces. I.e. easy to create a compositor with or without data_device_manager for instance. - DefaultCompositor provides a sensible default set of functionality and convenience methods for most test-cases. Custom ones can still be made by inheriting from CoreCompositor directly instead or by removing or adding globals to DefaultCompositor. - Globals have an isClean() method. Implement it to verify that the client didn't leave any objects lying around from the previous test. CoreCompositor::isClean calls isClean on the globals so a single call is all that's needed. In short, we've traded mock compositor encapsulation and thread safety guarantees for less boiler-plate, easier and more convenient access to internals. Anything accessing compositor state should go into a exec() call, or through the wrapper macros QCOMPOSITOR_VERIFY and QCOMPOSITOR_COMPARE (or the TRY versions). I've also tried to make the compositor print warnings if compositor state is accessed in an unsafe way. The mock compositor is currently built once per test due to CI limitations (same thing as with the old tests). Change-Id: Ia3feb80ce175d3814292b7f4768a0cc719f8b0e8 Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
Diffstat (limited to 'tests/auto/client/shared/mockcompositor.h')
-rw-r--r--tests/auto/client/shared/mockcompositor.h293
1 files changed, 43 insertions, 250 deletions
diff --git a/tests/auto/client/shared/mockcompositor.h b/tests/auto/client/shared/mockcompositor.h
index 4bab1ed67..07366a493 100644
--- a/tests/auto/client/shared/mockcompositor.h
+++ b/tests/auto/client/shared/mockcompositor.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
@@ -29,262 +29,55 @@
#ifndef MOCKCOMPOSITOR_H
#define MOCKCOMPOSITOR_H
-#include "mockxdgshellv6.h"
-#include "mockiviapplication.h"
-#include "mockfullscreenshellv1.h"
+#include "corecompositor.h"
+#include "coreprotocol.h"
+#include "xdgshell.h"
-#include <pthread.h>
-#include <qglobal.h>
-#include <wayland-server-core.h>
+#include <QtGui/QGuiApplication>
-#include <QImage>
-#include <QMutex>
-#include <QRect>
-#include <QSharedPointer>
-#include <QVariant>
-#include <QVector>
-#include <QWaitCondition>
-
-namespace Impl {
-
-typedef void (**Implementation)(void);
-
-class Keyboard;
-class Pointer;
-class Touch;
-class Seat;
-class DataDeviceManager;
-class Surface;
-class Output;
-class IviApplication;
-class WlShell;
-class XdgShellV6;
-
-class Compositor
-{
-public:
- Compositor();
- ~Compositor();
-
- int fileDescriptor() const { return m_fd; }
- void dispatchEvents(int timeout = 0);
-
- uint32_t nextSerial();
- uint32_t time() { return ++m_time; }
-
- QVector<Surface *> surfaces() const;
- QVector<Output *> outputs() const;
-
- IviApplication *iviApplication() const;
- XdgShellV6 *xdgShellV6() const;
- FullScreenShellV1 *fullScreenShellV1() const;
-
- void addSurface(Surface *surface);
- void removeSurface(Surface *surface);
-
- static void setKeyboardFocus(void *data, const QList<QVariant> &parameters);
- static void sendMousePress(void *data, const QList<QVariant> &parameters);
- static void sendMouseRelease(void *data, const QList<QVariant> &parameters);
- static void sendKeyPress(void *data, const QList<QVariant> &parameters);
- static void sendKeyRelease(void *data, const QList<QVariant> &parameters);
- static void sendTouchDown(void *data, const QList<QVariant> &parameters);
- static void sendTouchUp(void *data, const QList<QVariant> &parameters);
- static void sendTouchMotion(void *data, const QList<QVariant> &parameters);
- static void sendTouchFrame(void *data, const QList<QVariant> &parameters);
- static void sendDataDeviceDataOffer(void *data, const QList<QVariant> &parameters);
- static void sendDataDeviceEnter(void *data, const QList<QVariant> &parameters);
- static void sendDataDeviceMotion(void *data, const QList<QVariant> &parameters);
- static void sendDataDeviceDrop(void *data, const QList<QVariant> &parameters);
- static void sendDataDeviceLeave(void *data, const QList<QVariant> &parameters);
- static void waitForStartDrag(void *data, const QList<QVariant> &parameters);
- static void setOutputMode(void *compositor, const QList<QVariant> &parameters);
- static void sendAddOutput(void *data, const QList<QVariant> &parameters);
- static void sendRemoveOutput(void *data, const QList<QVariant> &parameters);
- static void sendOutputGeometry(void *data, const QList<QVariant> &parameters);
- static void sendSurfaceEnter(void *data, const QList<QVariant> &parameters);
- static void sendSurfaceLeave(void *data, const QList<QVariant> &parameters);
- static void sendShellSurfaceConfigure(void *data, const QList<QVariant> &parameters);
- static void sendIviSurfaceConfigure(void *data, const QList<QVariant> &parameters);
- static void sendXdgToplevelV6Configure(void *data, const QList<QVariant> &parameters);
-
-public:
- bool m_startDragSeen = false;
-
-private:
- static void bindCompositor(wl_client *client, void *data, uint32_t version, uint32_t id);
- static Surface *resolveSurface(const QVariant &v);
- static Output *resolveOutput(const QVariant &v);
- static IviSurface *resolveIviSurface(const QVariant &v);
- static XdgToplevelV6 *resolveToplevel(const QVariant &v);
-
- void initShm();
-
- QRect m_outputGeometry;
-
- wl_display *m_display = nullptr;
- wl_event_loop *m_loop = nullptr;
- int m_fd = -1;
-
- uint32_t m_time = 0;
-
- QScopedPointer<Seat> m_seat;
- Pointer *m_pointer = nullptr;
- Keyboard *m_keyboard = nullptr;
- Touch *m_touch = nullptr;
- QScopedPointer<DataDeviceManager> m_data_device_manager;
- QVector<Surface *> m_surfaces;
- QVector<Output *> m_outputs;
- QScopedPointer<IviApplication> m_iviApplication;
- QScopedPointer<WlShell> m_wlShell;
- QScopedPointer<XdgShellV6> m_xdgShellV6;
- QScopedPointer<FullScreenShellV1> m_fullScreenShellV1;
-};
-
-void registerResource(wl_list *list, wl_resource *resource);
-
-}
-
-class MockSurface
-{
-public:
- Impl::Surface *handle() const { return m_surface; }
-
- QImage image;
-
-private:
- MockSurface(Impl::Surface *surface);
- friend class Impl::Compositor;
- friend class Impl::Surface;
-
- Impl::Surface *m_surface = nullptr;
-};
-
-Q_DECLARE_METATYPE(QSharedPointer<MockSurface>)
-
-class MockIviSurface
-{
-public:
- Impl::IviSurface *handle() const { return m_iviSurface; }
- const uint iviId;
-
-private:
- MockIviSurface(Impl::IviSurface *iviSurface) : iviId(iviSurface->iviId()), m_iviSurface(iviSurface) {}
- friend class Impl::Compositor;
- friend class Impl::IviSurface;
-
- Impl::IviSurface *m_iviSurface;
-};
+#ifndef BTN_LEFT
+// As defined in linux/input-event-codes.h
+#define BTN_LEFT 0x110
+#endif
-Q_DECLARE_METATYPE(QSharedPointer<MockIviSurface>)
+namespace MockCompositor {
-class MockXdgToplevelV6 : public QObject
+class DefaultCompositor : public CoreCompositor
{
- Q_OBJECT
-public:
- Impl::XdgToplevelV6 *handle() const { return m_toplevel; }
-
- void sendConfigure(const QSharedPointer<MockXdgToplevelV6> toplevel);
-
-signals:
- uint setMinimizedRequested();
- uint setMaximizedRequested();
- uint unsetMaximizedRequested();
- uint setFullscreenRequested();
- uint unsetFullscreenRequested();
- void windowGeometryRequested(QRect geometry); // NOTE: This is really an xdg surface event
-
-private:
- MockXdgToplevelV6(Impl::XdgToplevelV6 *toplevel) : m_toplevel(toplevel) {}
- friend class Impl::Compositor;
- friend class Impl::XdgToplevelV6;
-
- Impl::XdgToplevelV6 *m_toplevel;
-};
-
-Q_DECLARE_METATYPE(QSharedPointer<MockXdgToplevelV6>)
-
-class MockOutput {
public:
- Impl::Output *handle() const { return m_output; }
- MockOutput(Impl::Output *output);
-private:
- Impl::Output *m_output = nullptr;
+ explicit DefaultCompositor();
+ // Convenience functions
+ Surface *surface(int i = 0) { return get<WlCompositor>()->m_surfaces.value(i, nullptr); }
+ XdgSurface *xdgSurface(int i = 0) { return get<XdgWmBase>()->m_xdgSurfaces.value(i, nullptr); }
+ XdgToplevel *xdgToplevel(int i = 0) { return get<XdgWmBase>()->toplevel(i); }
+ XdgPopup *xdgPopup(int i = 0) { return get<XdgWmBase>()->popup(i); }
+ Pointer *pointer() { auto *seat = get<Seat>(); Q_ASSERT(seat); return seat->m_pointer; }
+ uint sendXdgShellPing();
+ void xdgPingAndWaitForPong();
+ // Things that can be changed run-time without confusing the client (i.e. don't require separate tests)
+ struct Config {
+ bool autoEnter = true;
+ bool autoRelease = true;
+ bool autoConfigure = false;
+ } m_config;
+ void resetConfig() { exec([&] { m_config = Config{}; }); }
};
-Q_DECLARE_METATYPE(QSharedPointer<MockOutput>)
-
-class MockCompositor
-{
-public:
- MockCompositor();
- ~MockCompositor();
-
- void applicationInitialized();
-
- int waylandFileDescriptor() const;
- void processWaylandEvents();
-
- void setOutputMode(const QSize &size);
- void setKeyboardFocus(const QSharedPointer<MockSurface> &surface);
- void sendMousePress(const QSharedPointer<MockSurface> &surface, const QPoint &pos);
- void sendMouseRelease(const QSharedPointer<MockSurface> &surface);
- void sendKeyPress(const QSharedPointer<MockSurface> &surface, uint code);
- void sendKeyRelease(const QSharedPointer<MockSurface> &surface, uint code);
- void sendTouchDown(const QSharedPointer<MockSurface> &surface, const QPoint &position, int id);
- void sendTouchMotion(const QSharedPointer<MockSurface> &surface, const QPoint &position, int id);
- void sendTouchUp(const QSharedPointer<MockSurface> &surface, int id);
- void sendTouchFrame(const QSharedPointer<MockSurface> &surface);
- void sendDataDeviceDataOffer(const QSharedPointer<MockSurface> &surface);
- void sendDataDeviceEnter(const QSharedPointer<MockSurface> &surface, const QPoint &position);
- void sendDataDeviceMotion(const QPoint &position);
- void sendDataDeviceDrop(const QSharedPointer<MockSurface> &surface);
- void sendDataDeviceLeave(const QSharedPointer<MockSurface> &surface);
- void sendAddOutput();
- void sendRemoveOutput(const QSharedPointer<MockOutput> &output);
- void sendOutputGeometry(const QSharedPointer<MockOutput> &output, const QRect &geometry);
- void sendSurfaceEnter(const QSharedPointer<MockSurface> &surface, QSharedPointer<MockOutput> &output);
- void sendSurfaceLeave(const QSharedPointer<MockSurface> &surface, QSharedPointer<MockOutput> &output);
- void sendShellSurfaceConfigure(const QSharedPointer<MockSurface> surface, const QSize &size = QSize(0, 0));
- void sendIviSurfaceConfigure(const QSharedPointer<MockIviSurface> iviSurface, const QSize &size);
- void sendXdgToplevelV6Configure(const QSharedPointer<MockXdgToplevelV6> toplevel, const QSize &size = QSize(0, 0),
- const QVector<uint> &states = { ZXDG_TOPLEVEL_V6_STATE_ACTIVATED });
- void waitForStartDrag();
-
- QSharedPointer<MockSurface> surface();
- QSharedPointer<MockOutput> output(int index = 0);
- QSharedPointer<MockIviSurface> iviSurface(int index = 0);
- QSharedPointer<MockXdgToplevelV6> xdgToplevelV6(int index = 0);
- QSharedPointer<MockSurface> fullScreenShellV1Surface(int index = 0);
-
- void lock();
- void unlock();
-
-private:
- struct Command
- {
- typedef void (*Callback)(void *target, const QList<QVariant> &parameters);
-
- Callback callback;
- void *target = nullptr;
- QList<QVariant> parameters;
- };
-
- static Command makeCommand(Command::Callback callback, void *target);
-
- void processCommand(const Command &command);
- void dispatchCommands();
-
- static void *run(void *data);
-
- bool m_alive = true;
- bool m_ready = false;
- pthread_t m_thread;
- QMutex m_mutex;
- QWaitCondition m_waitCondition;
-
- Impl::Compositor *m_compositor = nullptr;
-
- QList<Command> m_commandQueue;
-};
+} // namespace MockCompositor
+
+#define QCOMPOSITOR_VERIFY(expr) QVERIFY(exec([&]{ return expr; }))
+#define QCOMPOSITOR_TRY_VERIFY(expr) QTRY_VERIFY(exec([&]{ return expr; }))
+#define QCOMPOSITOR_COMPARE(expr, expr2) QCOMPARE(exec([&]{ return expr; }), expr2)
+#define QCOMPOSITOR_TRY_COMPARE(expr, expr2) QTRY_COMPARE(exec([&]{ return expr; }), expr2)
+
+#define QCOMPOSITOR_TEST_MAIN(test) \
+int main(int argc, char **argv) \
+{ \
+ setenv("XDG_RUNTIME_DIR", ".", 1); \
+ setenv("QT_QPA_PLATFORM", "wayland", 1); \
+ test tc; \
+ QGuiApplication app(argc, argv); \
+ return QTest::qExec(&tc, argc, argv); \
+} \
#endif