From b3ccc12b75fe269445886c66bfaed63d3c3cf58c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Fri, 3 Jun 2016 11:24:36 +0200 Subject: [PATCH] [client] Detect errors in ConnectionThread Summary: So far ConnectionThread dispatched events without checking for (protocol) errors. But Wayland errors are considered fatal. We do need to consider them. This change introduces a way to detect errors and expose them in ConnectionThread. Errors are handled gracefully, they are exposed, but not considered application fatal. E.g. a new ConnectionThread could be created afterwards. This allows to add tests to verify error conditions. Reviewers: #plasma Subscribers: plasma-devel Tags: #plasma Differential Revision: https://phabricator.kde.org/D1753 --- src/wayland/autotests/client/CMakeLists.txt | 11 ++ src/wayland/autotests/client/test_error.cpp | 147 ++++++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 src/wayland/autotests/client/test_error.cpp diff --git a/src/wayland/autotests/client/CMakeLists.txt b/src/wayland/autotests/client/CMakeLists.txt index 48fc09badf..e33041255f 100644 --- a/src/wayland/autotests/client/CMakeLists.txt +++ b/src/wayland/autotests/client/CMakeLists.txt @@ -308,3 +308,14 @@ add_executable(testTextInput ${testTextInput_SRCS}) target_link_libraries( testTextInput Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer) add_test(kwayland-testTextInput testTextInput) ecm_mark_as_test(testTextInput) + +######################################################## +# Test Error +######################################################## +set( testError_SRCS + test_error.cpp + ) +add_executable(testError ${testError_SRCS}) +target_link_libraries( testError Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer) +add_test(kwayland-testError testError) +ecm_mark_as_test(testError) diff --git a/src/wayland/autotests/client/test_error.cpp b/src/wayland/autotests/client/test_error.cpp new file mode 100644 index 0000000000..e57c2efa1c --- /dev/null +++ b/src/wayland/autotests/client/test_error.cpp @@ -0,0 +1,147 @@ +/******************************************************************** +Copyright 2016 Martin Gräßlin + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +*********************************************************************/ +// Qt +#include +// client +#include "../../src/client/compositor.h" +#include "../../src/client/connection_thread.h" +#include "../../src/client/event_queue.h" +#include "../../src/client/registry.h" +#include "../../src/client/shell.h" +#include "../../src/client/surface.h" +// server +#include "../../src/server/display.h" +#include "../../src/server/compositor_interface.h" +#include "../../src/server/shell_interface.h" + +using namespace KWayland::Client; +using namespace KWayland::Server; + +class ErrorTest : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void init(); + void cleanup(); + + void testMultipleShellSurfacesForSurface(); + +private: + Display *m_display = nullptr; + CompositorInterface *m_ci = nullptr; + ShellInterface *m_si = nullptr; + ConnectionThread *m_connection = nullptr; + QThread *m_thread = nullptr; + EventQueue *m_queue = nullptr; + Compositor *m_compositor = nullptr; + Shell *m_shell = nullptr; +}; + +static const QString s_socketName = QStringLiteral("kwayland-test-error-0"); + +void ErrorTest::init() +{ + delete m_display; + m_display = new Display(this); + m_display->setSocketName(s_socketName); + m_display->start(); + QVERIFY(m_display->isRunning()); + m_display->createShm(); + m_ci = m_display->createCompositor(m_display); + m_ci->create(); + m_si = m_display->createShell(m_display); + m_si->create(); + + // setup connection + m_connection = new KWayland::Client::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); + m_queue->setup(m_connection); + + Registry registry; + QSignalSpy interfacesAnnouncedSpy(®istry, &Registry::interfacesAnnounced); + QVERIFY(interfacesAnnouncedSpy.isValid()); + registry.setEventQueue(m_queue); + registry.create(m_connection); + QVERIFY(registry.isValid()); + registry.setup(); + QVERIFY(interfacesAnnouncedSpy.wait()); + + m_compositor = registry.createCompositor(registry.interface(Registry::Interface::Compositor).name, + registry.interface(Registry::Interface::Compositor).version, + this); + QVERIFY(m_compositor); + m_shell = registry.createShell(registry.interface(Registry::Interface::Shell).name, + registry.interface(Registry::Interface::Shell).version, + this); + QVERIFY(m_shell); +} + +void ErrorTest::cleanup() +{ +#define CLEANUP(variable) \ + if (variable) { \ + delete variable; \ + variable = nullptr; \ + } + CLEANUP(m_shell) + CLEANUP(m_compositor) + CLEANUP(m_queue) + if (m_connection) { + m_connection->deleteLater(); + m_connection = nullptr; + } + if (m_thread) { + m_thread->quit(); + m_thread->wait(); + delete m_thread; + m_thread = nullptr; + } + CLEANUP(m_si) + CLEANUP(m_ci) + CLEANUP(m_display) +#undef CLEANUP +} + +void ErrorTest::testMultipleShellSurfacesForSurface() +{ + // this test verifies that creating two ShellSurfaces for the same Surface triggers a protocol error + QSignalSpy errorSpy(m_connection, &ConnectionThread::errorOccurred); + QVERIFY(errorSpy.isValid()); + QScopedPointer surface(m_compositor->createSurface()); + QScopedPointer shellSurface1(m_shell->createSurface(surface.data())); + QScopedPointer shellSurface2(m_shell->createSurface(surface.data())); + QVERIFY(errorSpy.wait()); + QVERIFY(m_connection->hasError()); + QCOMPARE(m_connection->errorCode(), EPROTO); +} + +QTEST_GUILESS_MAIN(ErrorTest) +#include "test_error.moc"