summaryrefslogtreecommitdiffstats
path: root/tests/auto/gui/platform
diff options
context:
space:
mode:
authorTor Arne Vestbø <tor.arne.vestbo@qt.io>2021-05-10 13:03:38 +0200
committerLiang Qi <liang.qi@qt.io>2021-05-10 21:19:46 +0000
commitedceff30b4093e2bef6e80c7552edf595134f351 (patch)
treecefb89ea882051550614adbc2731247ca095896c /tests/auto/gui/platform
parent066a1de8e7b198dbe99cdecefeae94d5f0e5f289 (diff)
Move QtX11Extras into QtGui as private API
from qt/qtx11extras 0e67fb41cfc4b4bfbaa7dc75f8ddebdf5a08e836. The plan is to expose these as native interfaces, so this is a first step. Task-number: QTBUG-83251 Change-Id: Iecba8db9a4f616a08a3750ddaae08cc30ec66f89 Reviewed-by: Liang Qi <liang.qi@qt.io>
Diffstat (limited to 'tests/auto/gui/platform')
-rw-r--r--tests/auto/gui/platform/CMakeLists.txt3
-rw-r--r--tests/auto/gui/platform/qx11info/CMakeLists.txt7
-rw-r--r--tests/auto/gui/platform/qx11info/tst_qx11info.cpp390
3 files changed, 400 insertions, 0 deletions
diff --git a/tests/auto/gui/platform/CMakeLists.txt b/tests/auto/gui/platform/CMakeLists.txt
new file mode 100644
index 0000000000..fc2d330fd7
--- /dev/null
+++ b/tests/auto/gui/platform/CMakeLists.txt
@@ -0,0 +1,3 @@
+if(QT_FEATURE_xcb)
+ add_subdirectory(qx11info)
+endif()
diff --git a/tests/auto/gui/platform/qx11info/CMakeLists.txt b/tests/auto/gui/platform/qx11info/CMakeLists.txt
new file mode 100644
index 0000000000..c4ce1f91c8
--- /dev/null
+++ b/tests/auto/gui/platform/qx11info/CMakeLists.txt
@@ -0,0 +1,7 @@
+qt_internal_add_test(tst_qx11info
+ SOURCES
+ tst_qx11info.cpp
+ PUBLIC_LIBRARIES
+ Qt::GuiPrivate
+ XCB::XCB
+)
diff --git a/tests/auto/gui/platform/qx11info/tst_qx11info.cpp b/tests/auto/gui/platform/qx11info/tst_qx11info.cpp
new file mode 100644
index 0000000000..0cd11c87ac
--- /dev/null
+++ b/tests/auto/gui/platform/qx11info/tst_qx11info.cpp
@@ -0,0 +1,390 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2016 David Faure <david.faure@kdab.com>
+** 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 <QtGui>
+#include <QtTest/QtTest>
+
+#include <QtGui/private/qtx11extras_p.h>
+
+class tst_QX11Info : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void staticFunctionsBeforeQGuiApplication();
+ void startupId();
+ void isPlatformX11();
+ void appTime();
+ void peeker();
+ void isCompositingManagerRunning();
+};
+
+void tst_QX11Info::staticFunctionsBeforeQGuiApplication()
+{
+ QVERIFY(!QGuiApplication::instance());
+
+ // none of these functions should crash if QGuiApplication hasn't
+ // been constructed
+
+ Display *display = QX11Info::display();
+ QCOMPARE(display, (Display *)0);
+
+#if 0
+ const char *appClass = QX11Info::appClass();
+ QCOMPARE(appClass, (const char *)0);
+#endif
+ int appScreen = QX11Info::appScreen();
+ QCOMPARE(appScreen, 0);
+#if 0
+ int appDepth = QX11Info::appDepth();
+ QCOMPARE(appDepth, 32);
+ int appCells = QX11Info::appCells();
+ QCOMPARE(appCells, 0);
+ Qt::HANDLE appColormap = QX11Info::appColormap();
+ QCOMPARE(appColormap, static_cast<Qt::HANDLE>(0));
+ void *appVisual = QX11Info::appVisual();
+ QCOMPARE(appVisual, static_cast<void *>(0));
+#endif
+ unsigned long appRootWindow = QX11Info::appRootWindow();
+ QCOMPARE(appRootWindow, static_cast<unsigned long>(0));
+
+#if 0
+ bool appDefaultColormap = QX11Info::appDefaultColormap();
+ QCOMPARE(appDefaultColormap, true);
+ bool appDefaultVisual = QX11Info::appDefaultVisual();
+ QCOMPARE(appDefaultVisual, true);
+#endif
+
+ int appDpiX = QX11Info::appDpiX();
+ int appDpiY = QX11Info::appDpiY();
+ QCOMPARE(appDpiX, 75);
+ QCOMPARE(appDpiY, 75);
+
+ unsigned long appTime = QX11Info::appTime();
+ unsigned long appUserTime = QX11Info::appUserTime();
+ QCOMPARE(appTime, 0ul);
+ QCOMPARE(appUserTime, 0ul);
+ // setApp*Time do nothing without QGuiApplication
+ QX11Info::setAppTime(1234);
+ QX11Info::setAppUserTime(5678);
+ appTime = QX11Info::appTime();
+ appUserTime = QX11Info::appUserTime();
+ QCOMPARE(appTime, 0ul);
+ QCOMPARE(appTime, 0ul);
+
+ QX11Info::isCompositingManagerRunning();
+}
+
+static const char idFromEnv[] = "startupid_TIME123456";
+void initialize()
+{
+ qputenv("DESKTOP_STARTUP_ID", idFromEnv);
+}
+Q_CONSTRUCTOR_FUNCTION(initialize)
+
+void tst_QX11Info::startupId()
+{
+ int argc = 0;
+ QGuiApplication app(argc, 0);
+
+ // This relies on the fact that no widget was shown yet,
+ // so please make sure this method is always the first test.
+ QCOMPARE(QString(QX11Info::nextStartupId()), QString(idFromEnv));
+ QWindow w;
+ w.show();
+ QVERIFY(QX11Info::nextStartupId().isEmpty());
+
+ QByteArray idSecondWindow = "startupid2_TIME234567";
+ QX11Info::setNextStartupId(idSecondWindow);
+ QCOMPARE(QX11Info::nextStartupId(), idSecondWindow);
+
+ QWindow w2;
+ w2.show();
+ QVERIFY(QX11Info::nextStartupId().isEmpty());
+}
+
+void tst_QX11Info::isPlatformX11()
+{
+ int argc = 0;
+ QGuiApplication app(argc, 0);
+
+ QVERIFY(QX11Info::isPlatformX11());
+}
+
+void tst_QX11Info::appTime()
+{
+ int argc = 0;
+ QGuiApplication app(argc, 0);
+
+ // No X11 event received yet
+ QCOMPARE(QX11Info::appTime(), 0ul);
+ QCOMPARE(QX11Info::appUserTime(), 0ul);
+
+ // Trigger some X11 events
+ QWindow window;
+ window.show();
+ QTest::qWait(100);
+ QVERIFY(QX11Info::appTime() > 0);
+
+ unsigned long t0 = QX11Info::appTime();
+ unsigned long t1 = t0 + 1;
+ QX11Info::setAppTime(t1);
+ QCOMPARE(QX11Info::appTime(), t1);
+
+ unsigned long u0 = QX11Info::appUserTime();
+ unsigned long u1 = u0 + 1;
+ QX11Info::setAppUserTime(u1);
+ QCOMPARE(QX11Info::appUserTime(), u1);
+}
+
+class PeekerTest : public QWindow
+{
+public:
+ PeekerTest()
+ {
+ setGeometry(100, 100, 400, 400);
+ m_peekerFirstId = QX11Info::generatePeekerId();
+ QVERIFY(m_peekerFirstId >= 0);
+ m_peekerSecondId = QX11Info::generatePeekerId();
+ QVERIFY(m_peekerSecondId == m_peekerFirstId + 1);
+ // Get X atoms
+ xcb_connection_t *c = QX11Info::connection();
+ const char *first = "QT_XCB_PEEKER_TEST_FIRST";
+ const char *second = "QT_XCB_PEEKER_TEST_SECOND";
+ xcb_intern_atom_reply_t *reply =
+ xcb_intern_atom_reply(c, xcb_intern_atom(c, false, strlen(first), first), 0);
+ QVERIFY2(reply != nullptr, first);
+ m_atomFirst = reply->atom;
+ free(reply);
+ reply = xcb_intern_atom_reply(c, xcb_intern_atom(c, false, strlen(second), second), 0);
+ QVERIFY2(reply != nullptr, second);
+ m_atomSecond = reply->atom;
+ free(reply);
+ }
+
+protected:
+ void triggerPropertyNotifyEvents()
+ {
+ xcb_window_t rootWindow = QX11Info::appRootWindow();
+ xcb_connection_t *connection = QX11Info::connection();
+ xcb_change_property(connection, XCB_PROP_MODE_APPEND, rootWindow, m_atomFirst,
+ XCB_ATOM_INTEGER, 32, 0, nullptr);
+ xcb_change_property(connection, XCB_PROP_MODE_APPEND, rootWindow, m_atomSecond,
+ XCB_ATOM_INTEGER, 32, 0, nullptr);
+ xcb_flush(connection);
+ }
+
+ static bool checkForPropertyNotifyByAtom(xcb_generic_event_t *event, void *data)
+ {
+ bool isPropertyNotify = (event->response_type & ~0x80) == XCB_PROPERTY_NOTIFY;
+ if (isPropertyNotify) {
+ xcb_property_notify_event_t *pn =
+ reinterpret_cast<xcb_property_notify_event_t *>(event);
+ xcb_atom_t *atom = static_cast<xcb_atom_t *>(data);
+ if (pn->atom == *atom)
+ return true;
+ }
+ return false;
+ }
+
+ bool sanityCheckPeeking(xcb_generic_event_t *event)
+ {
+ m_countWithCaching++;
+ bool isPropertyNotify = (event->response_type & ~0x80) == XCB_PROPERTY_NOTIFY;
+ if (isPropertyNotify) {
+ xcb_property_notify_event_t *pn =
+ reinterpret_cast<xcb_property_notify_event_t *>(event);
+ if (pn->atom == m_atomFirst) {
+ if (m_indexFirst == -1) {
+ m_indexFirst = m_countWithCaching;
+ // continue peeking, maybe the second event is already in the queue
+ return false;
+ }
+ m_foundFirstEventAgain = true;
+ // Return so we can fail the test with QVERIFY2, for more details
+ // see QTBUG-62354
+ return true;
+ }
+ // Let it peek to the end, even when the second event was found
+ if (pn->atom == m_atomSecond) {
+ m_indexSecond = m_countWithCaching;
+ if (m_indexFirst == -1) {
+ m_foundSecondBeforeFirst = true;
+ // Same as above, see QTBUG-62354
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ void exposeEvent(QExposeEvent *) override
+ {
+ if (m_ignoreSubsequentExpose)
+ return;
+ // We don't want to execute this handler again in case there are more expose
+ // events after calling QCoreApplication::processEvents() later in this test
+ m_ignoreSubsequentExpose = true;
+
+ xcb_flush(QX11Info::connection());
+ bool found = QX11Info::peekEventQueue(checkForPropertyNotifyByAtom, &m_atomFirst);
+ QVERIFY2(!found, "Found m_atomFirst which should not be in the queue yet");
+ found = QX11Info::peekEventQueue(checkForPropertyNotifyByAtom, &m_atomSecond);
+ QVERIFY2(!found, "Found m_atomSecond which should not be in the queue yet");
+
+ triggerPropertyNotifyEvents();
+
+ bool earlyExit = false;
+ while (!earlyExit && (m_indexFirst == -1 || m_indexSecond == -1)) {
+
+ earlyExit = QX11Info::peekEventQueue([](xcb_generic_event_t *event, void *data) {
+ return static_cast<PeekerTest *>(data)->sanityCheckPeeking(event);
+ }, this, QX11Info::PeekFromCachedIndex, m_peekerFirstId);
+
+ if (m_countWithCaching == -1)
+ QVERIFY2(!earlyExit, "Unexpected return value for an empty queue");
+ }
+
+ QVERIFY2(!m_foundFirstEventAgain,
+ "Found the same notify event twice, maybe broken index cache?");
+ QVERIFY2(!m_foundSecondBeforeFirst, "Found second event before first");
+ QVERIFY2(!earlyExit,
+ "Unexpected return value for a peeker that always scans to the end of the queue");
+
+ found = QX11Info::peekEventQueue(checkForPropertyNotifyByAtom, &m_atomFirst,
+ QX11Info::PeekDefault, m_peekerFirstId);
+ QVERIFY2(found, "Failed to find m_atomFirst, when peeking from start and ignoring "
+ "the cached index associated with the passed in peeker id");
+ // The above call updated index cache for m_peekerFirstId to the position of
+ // event(m_atomFirst) + 1
+ found = QX11Info::peekEventQueue(checkForPropertyNotifyByAtom, &m_atomSecond,
+ QX11Info::PeekFromCachedIndex, m_peekerFirstId);
+ QVERIFY2(found, "Unexpectedly failed to find m_atomSecond");
+
+ QVERIFY(m_indexFirst <= m_countWithCaching);
+ QVERIFY(m_indexSecond <= m_countWithCaching);
+ QVERIFY(m_indexFirst < m_indexSecond);
+ QX11Info::peekEventQueue([](xcb_generic_event_t *, void *data) {
+ static_cast<PeekerTest *>(data)->m_countFromStart++;
+ return false;
+ }, this);
+ QVERIFY(m_countWithCaching <= m_countFromStart);
+ found = QX11Info::peekEventQueue(checkForPropertyNotifyByAtom, &m_atomFirst,
+ QX11Info::PeekFromCachedIndex, m_peekerSecondId);
+ QVERIFY2(found, "m_peekerSecondId failed to find the event");
+
+ // Remove peeker id from within the peeker while using it for peeking
+ QX11Info::peekEventQueue([](xcb_generic_event_t *, void *data) {
+ PeekerTest *obj = static_cast<PeekerTest *>(data);
+ QX11Info::removePeekerId(obj->m_peekerSecondId);
+ return true;
+ }, this, QX11Info::PeekFromCachedIndex, m_peekerSecondId);
+ // Check that it really has been removed from the cache
+ bool ok = QX11Info::peekEventQueue([](xcb_generic_event_t *, void *) {
+ return true;
+ }, nullptr, QX11Info::PeekFromCachedIndex, m_peekerSecondId);
+ QVERIFY2(!ok, "Unexpected return value when attempting to peek from cached "
+ "index when peeker id has been removed from the cache");
+
+ // Sanity check other input combinations
+ QVERIFY(!QX11Info::removePeekerId(-99));
+ ok = QX11Info::peekEventQueue([](xcb_generic_event_t *, void *) {
+ return true;
+ }, nullptr, QX11Info::PeekFromCachedIndex, -100);
+ QVERIFY2(!ok, "PeekFromCachedIndex with invalid peeker id unexpectedly succeeded");
+ ok = QX11Info::peekEventQueue([](xcb_generic_event_t *, void *) {
+ return true;
+ }, nullptr, QX11Info::PeekDefault, -100);
+ QVERIFY2(!ok, "PeekDefault with invalid peeker id unexpectedly succeeded");
+ ok = QX11Info::peekEventQueue([](xcb_generic_event_t *, void *) {
+ return true;
+ }, nullptr, QX11Info::PeekFromCachedIndex);
+ QVERIFY2(!ok, "PeekFromCachedIndex without peeker id unexpectedly succeeded");
+ ok = QX11Info::peekEventQueue([](xcb_generic_event_t *, void *) {
+ return true;
+ }, nullptr, QX11Info::PeekDefault);
+ QVERIFY2(ok, "PeekDefault without peeker id unexpectedly failed");
+
+ QCoreApplication::processEvents(); // Flush buffered events
+
+ found = QX11Info::peekEventQueue(checkForPropertyNotifyByAtom, &m_atomFirst);
+ QVERIFY2(!found, "Found m_atomFirst in the queue after flushing");
+ found = QX11Info::peekEventQueue(checkForPropertyNotifyByAtom, &m_atomSecond);
+ QVERIFY2(!found, "Found m_atomSecond in the queue after flushing");
+
+ QVERIFY(QX11Info::removePeekerId(m_peekerFirstId));
+ QVERIFY2(!QX11Info::removePeekerId(m_peekerFirstId),
+ "Removing the same peeker id twice unexpectedly succeeded");
+#if 0
+ qDebug() << "Buffered event queue size (caching) : " << m_countWithCaching + 1;
+ qDebug() << "Buffered event queue size (from start) : " << m_countFromStart + 1;
+ qDebug() << "PropertyNotify[FIRST] at : " << m_indexFirst;
+ qDebug() << "PropertyNotify[SECOND] at : " << m_indexSecond;
+#endif
+ }
+
+private:
+ xcb_atom_t m_atomFirst = -1;
+ xcb_atom_t m_atomSecond = -1;
+ qint32 m_peekerFirstId = -1;
+ qint32 m_peekerSecondId = -1;
+ qint32 m_indexFirst = -1;
+ qint32 m_indexSecond = -1;
+ bool m_foundFirstEventAgain = false;
+ qint32 m_countWithCaching = -1;
+ qint32 m_countFromStart = -1;
+ bool m_ignoreSubsequentExpose = false;
+ bool m_foundSecondBeforeFirst = false;
+};
+
+void tst_QX11Info::peeker()
+{
+ int argc = 0;
+ QGuiApplication app(argc, 0);
+
+ PeekerTest test;
+ test.show();
+
+ QVERIFY(QTest::qWaitForWindowExposed(&test));
+}
+
+void tst_QX11Info::isCompositingManagerRunning()
+{
+ int argc = 0;
+ QGuiApplication app(argc, 0);
+ const bool b = QX11Info::isCompositingManagerRunning();
+ Q_UNUSED(b);
+ const bool b2 = QX11Info::isCompositingManagerRunning(0);
+ Q_UNUSED(b2);
+ // just check that it didn't crash (QTBUG-91913)
+}
+
+QTEST_APPLESS_MAIN(tst_QX11Info)
+
+#include "tst_qx11info.moc"