diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/auto/client/CMakeLists.txt | 1 | ||||
-rw-r--r-- | tests/auto/client/multithreaded/CMakeLists.txt | 12 | ||||
-rw-r--r-- | tests/auto/client/multithreaded/tst_multithreaded.cpp | 165 |
3 files changed, 178 insertions, 0 deletions
diff --git a/tests/auto/client/CMakeLists.txt b/tests/auto/client/CMakeLists.txt index ea6c4c1b..167cc607 100644 --- a/tests/auto/client/CMakeLists.txt +++ b/tests/auto/client/CMakeLists.txt @@ -6,6 +6,7 @@ add_subdirectory(client) add_subdirectory(datadevicev1) add_subdirectory(fullscreenshellv1) add_subdirectory(iviapplication) +add_subdirectory(multithreaded) add_subdirectory(nooutput) add_subdirectory(output) add_subdirectory(primaryselectionv1) diff --git a/tests/auto/client/multithreaded/CMakeLists.txt b/tests/auto/client/multithreaded/CMakeLists.txt new file mode 100644 index 00000000..49d93297 --- /dev/null +++ b/tests/auto/client/multithreaded/CMakeLists.txt @@ -0,0 +1,12 @@ +# Generated from multithreaded.pro. + +##################################################################### +## tst_multithreaded Test: +##################################################################### + +qt_internal_add_test(tst_multithreaded + SOURCES + tst_multithreaded.cpp + PUBLIC_LIBRARIES + SharedClientTest +) diff --git a/tests/auto/client/multithreaded/tst_multithreaded.cpp b/tests/auto/client/multithreaded/tst_multithreaded.cpp new file mode 100644 index 00000000..ce88a72f --- /dev/null +++ b/tests/auto/client/multithreaded/tst_multithreaded.cpp @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Copyright (C) 2020 UBports Foundataion. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <unistd.h> +#include <poll.h> + +#include <wayland-client-core.h> + +#include <QtGui/QScreen> +#include <QAbstractEventDispatcher> +#include <qpa/qplatformnativeinterface.h> + +#include "mockcompositor.h" + +using namespace MockCompositor; + +/* + * This class simulate a thread from another library which use poll() to wait + * for data from Wayland compositor. + */ + +class ExternalWaylandReaderThread : public QThread +{ +public: + ExternalWaylandReaderThread(struct wl_display *disp) + : QThread() + , m_disp(disp) + { + setObjectName(QStringLiteral("ExternalWaylandReader")); + } + + ~ExternalWaylandReaderThread() + { + if (m_pipefd[1] != -1 && write(m_pipefd[1], "q", 1) == -1) + qWarning("Failed to write to the pipe: %s.", strerror(errno)); + + wait(); + } + +protected: + void run() override + { + // we use this pipe to make the loop exit otherwise if we simply used a flag on the loop condition, if stop() gets + // called while poll() is blocking the thread will never quit since there are no wayland messages coming anymore. + struct Pipe + { + Pipe(int *fds) + : fds(fds) + { + if (::pipe(fds) != 0) + qWarning("Pipe creation failed. Quitting may hang."); + } + ~Pipe() + { + if (fds[0] != -1) { + close(fds[0]); + close(fds[1]); + } + } + + int *fds; + } pipe(m_pipefd); + + struct wl_event_queue *a_queue = wl_display_create_queue(m_disp); + struct pollfd fds[2] = { { wl_display_get_fd(m_disp), POLLIN, 0 }, + { m_pipefd[0], POLLIN, 0 } }; + + while (true) { + // No wl_proxy is assigned to this queue, thus guaranteed to be always empty. + Q_ASSERT(wl_display_prepare_read_queue(m_disp, a_queue) == 0); + wl_display_flush(m_disp); + + // Wakeup every 10 seconds so that if Qt blocks in _read_events(), + // it won't last forever. + poll(fds, /* nfds */ 2, 10000); + + if (fds[0].revents & POLLIN) { + wl_display_read_events(m_disp); + } else { + wl_display_cancel_read(m_disp); + } + + if (fds[1].revents & POLLIN) { + char pipeIn; + read(m_pipefd[0], &pipeIn, 1); + if (pipeIn == 'q') + break; + } + } + + wl_event_queue_destroy(a_queue); + } + +private: + struct wl_display *m_disp; + int m_pipefd[2] = { -1, -1 }; +}; + +class tst_multithreaded : public QObject, private DefaultCompositor +{ + Q_OBJECT +private slots: + void initTestCase() + { + m_config.autoConfigure = true; + m_config.autoEnter = false; + } + void init() + { + // a test case is given new simulated thread. + QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface(); + struct wl_display *wl_dpy = + (struct wl_display *)native->nativeResourceForWindow("display", NULL); + + m_extThread.reset(new ExternalWaylandReaderThread(wl_dpy)); + m_extThread->start(); + } + void cleanup() + { + QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage())); + } + + void mainThreadIsNotBlocked(); + +public: + QScopedPointer<ExternalWaylandReaderThread> m_extThread; +}; + +void tst_multithreaded::mainThreadIsNotBlocked() +{ + QElapsedTimer timer; + timer.start(); + + QTest::qWait(100); + QVERIFY(timer.elapsed() < 200); +} + +QCOMPOSITOR_TEST_MAIN(tst_multithreaded) +#include "tst_multithreaded.moc" |