diff options
Diffstat (limited to 'tests/auto/widgets/widgets/qopenglwidget')
3 files changed, 321 insertions, 90 deletions
diff --git a/tests/auto/widgets/widgets/qopenglwidget/BLACKLIST b/tests/auto/widgets/widgets/qopenglwidget/BLACKLIST index ecb962d8ca..6b96499889 100644 --- a/tests/auto/widgets/widgets/qopenglwidget/BLACKLIST +++ b/tests/auto/widgets/widgets/qopenglwidget/BLACKLIST @@ -1,5 +1,7 @@ -[stackWidgetOpaqueChildIsVisible] -windows-10 msvc-2017 +# Temporary solution COIN-966 +ubuntu +rhel +opensuse-leap # QTBUG-87436 [clearAndGrab] diff --git a/tests/auto/widgets/widgets/qopenglwidget/CMakeLists.txt b/tests/auto/widgets/widgets/qopenglwidget/CMakeLists.txt index 1079004e3a..78cef5300a 100644 --- a/tests/auto/widgets/widgets/qopenglwidget/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qopenglwidget/CMakeLists.txt @@ -1,14 +1,18 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -# Generated from qopenglwidget.pro. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qopenglwidget Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qopenglwidget LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qopenglwidget - LOWDPI # special case + LOWDPI SOURCES tst_qopenglwidget.cpp LIBRARIES diff --git a/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp b/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp index 9c0ce637c3..51f898c953 100644 --- a/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp +++ b/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp @@ -1,9 +1,10 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtOpenGLWidgets/QOpenGLWidget> #include <QtGui/QOpenGLFunctions> #include <QtGui/QPainter> +#include <QtGui/QBackingStore> #include <QtGui/QScreen> #include <QtGui/QStaticText> #include <QtWidgets/QGraphicsView> @@ -21,7 +22,9 @@ #include <private/qopengltextureglyphcache_p.h> #include <qpa/qplatformintegration.h> #include <private/qguiapplication_p.h> +#include <qpa/qplatformbackingstore.h> #include <qpa/qplatformintegration.h> +#include <rhi/qrhi.h> class tst_QOpenGLWidget : public QObject { @@ -33,6 +36,9 @@ private slots: void clearAndGrab(); void clearAndResizeAndGrab(); void createNonTopLevel(); +#if QT_CONFIG(egl) + void deviceLoss(); +#endif void painter(); void reparentToAlreadyCreated(); void reparentToNotYetCreated(); @@ -47,6 +53,8 @@ private slots: void offscreen(); void offscreenThenOnscreen(); void paintWhileHidden(); + void widgetWindowColorFormat_data(); + void widgetWindowColorFormat(); #ifdef QT_BUILD_INTERNAL void staticTextDanglingPointer(); @@ -68,7 +76,7 @@ void tst_QOpenGLWidget::create() QSignalSpy frameSwappedSpy(w.data(), SIGNAL(frameSwapped())); w->show(); QVERIFY(QTest::qWaitForWindowExposed(w.data())); - QVERIFY(frameSwappedSpy.count() > 0); + QVERIFY(frameSwappedSpy.size() > 0); QVERIFY(w->isValid()); QVERIFY(w->context()); @@ -163,7 +171,7 @@ void tst_QOpenGLWidget::createNonTopLevel() w.resize(400, 400); w.show(); QVERIFY(QTest::qWaitForWindowExposed(&w)); - QVERIFY(frameSwappedSpy.count() > 0); + QVERIFY(frameSwappedSpy.size() > 0); QVERIFY(glw->m_resizeCalled); glw->m_resizeCalled = false; @@ -187,6 +195,45 @@ void tst_QOpenGLWidget::createNonTopLevel() QVERIFY(QOpenGLContext::currentContext() == glw->context() && glw->context()); } +#if QT_CONFIG(egl) +void tst_QOpenGLWidget::deviceLoss() +{ + QScopedPointer<QOpenGLWidget> w(new ClearWidget(0, 640, 480)); + + w->resize(640, 480); + w->show(); + + auto rhi = w->backingStore()->handle()->rhi(w->windowHandle()); + QNativeInterface::QEGLContext *rhiContext = nullptr; + if (rhi->backend() == QRhi::OpenGLES2) { + auto rhiHandles = static_cast<const QRhiGles2NativeHandles *>(rhi->nativeHandles()); + rhiContext = rhiHandles->context->nativeInterface<QNativeInterface::QEGLContext>(); + } + if (!rhiContext) + QSKIP("deviceLoss needs EGL"); + + QVERIFY(QTest::qWaitForWindowExposed(w.data())); + + QImage image = w->grabFramebuffer(); + QVERIFY(!image.isNull()); + QCOMPARE(image.width(), w->width()); + QCOMPARE(image.height(), w->height()); + QVERIFY(image.pixel(30, 40) == qRgb(255, 0, 0)); + + rhiContext->invalidateContext(); + + w->resize(600, 600); + QSignalSpy frameSwappedSpy(w.get(), &QOpenGLWidget::resized); + QTRY_VERIFY(frameSwappedSpy.size() > 0); + + image = w->grabFramebuffer(); + QVERIFY(!image.isNull()); + QCOMPARE(image.width(), w->width()); + QCOMPARE(image.height(), w->height()); + QVERIFY(image.pixel(30, 40) == qRgb(255, 0, 0)); +} +#endif + class PainterWidget : public QOpenGLWidget, protected QOpenGLFunctions { public: @@ -324,7 +371,7 @@ void tst_QOpenGLWidget::reparentTopLevel() { QSignalSpy frameSwappedSpy(glw1, &QOpenGLWidget::frameSwapped); tabWidget.setCurrentIndex(tabWidget.addTab(glw1, "OpenGL widget 1")); - QTRY_VERIFY(frameSwappedSpy.count() > 0); + QTRY_VERIFY(frameSwappedSpy.size() > 0); } PainterWidget *glw2 = new PainterWidget; @@ -332,7 +379,7 @@ void tst_QOpenGLWidget::reparentTopLevel() { QSignalSpy frameSwappedSpy(glw2, &QOpenGLWidget::frameSwapped); tabWidget.setCurrentIndex(tabWidget.addTab(glw2, "OpenGL widget 2")); - QTRY_VERIFY(frameSwappedSpy.count() > 0); + QTRY_VERIFY(frameSwappedSpy.size() > 0); } QImage image = glw2->grabFramebuffer(); @@ -342,7 +389,7 @@ void tst_QOpenGLWidget::reparentTopLevel() { QSignalSpy frameSwappedSpy(glw1, &QOpenGLWidget::frameSwapped); delete glw2; - QTRY_VERIFY(frameSwappedSpy.count() > 0); + QTRY_VERIFY(frameSwappedSpy.size() > 0); } image = glw1->grabFramebuffer(); @@ -354,7 +401,7 @@ void tst_QOpenGLWidget::reparentTopLevel() glw1->setParent(nullptr); glw1->show(); QVERIFY(QTest::qWaitForWindowExposed(glw1)); - QTRY_VERIFY(frameSwappedSpy.count() > 0); + QTRY_VERIFY(frameSwappedSpy.size() > 0); } image = glw1->grabFramebuffer(); @@ -364,7 +411,7 @@ void tst_QOpenGLWidget::reparentTopLevel() { QSignalSpy frameSwappedSpy(glw1, &QOpenGLWidget::frameSwapped); tabWidget.setCurrentIndex(tabWidget.addTab(glw1, "Re-added OpenGL widget 1")); - QTRY_VERIFY(frameSwappedSpy.count() > 0); + QTRY_VERIFY(frameSwappedSpy.size() > 0); } image = glw1->grabFramebuffer(); @@ -421,6 +468,7 @@ void tst_QOpenGLWidget::asViewport() // repainted when going from Inactive to Active. So wait for the window to be // active before we continue, so the activation doesn't happen at a random // time below. And call processEvents to have the paint events delivered right away. + widget.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&widget)); qApp->processEvents(); } @@ -521,44 +569,187 @@ void tst_QOpenGLWidget::showHide() QVERIFY(image.pixel(30, 40) == qRgb(0, 0, 255)); } +QtMessageHandler oldHandler = nullptr; + +void nativeWindowMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) +{ + if (oldHandler) + oldHandler(type, context, msg); + + if (type == QtWarningMsg + && (msg.contains("QOpenGLContext::makeCurrent() called with non-opengl surface") + || msg.contains("Failed to make context current"))) + { + QFAIL("Unexpected warning got printed"); + } +} + void tst_QOpenGLWidget::nativeWindow() { #ifdef Q_OS_ANDROID QSKIP("Crashes on Android, figure out why (QTBUG-102043)"); #endif - QScopedPointer<ClearWidget> w(new ClearWidget(0, 800, 600)); - w->resize(800, 600); - w->show(); - w->winId(); - QVERIFY(QTest::qWaitForWindowExposed(w.data())); - QImage image = w->grabFramebuffer(); - QVERIFY(!image.isNull()); - QCOMPARE(image.width(), w->width()); - QCOMPARE(image.height(), w->height()); - QVERIFY(image.pixel(30, 40) == qRgb(255, 0, 0)); - QVERIFY(w->internalWinId()); - - // Now as a native child. - QWidget nativeParent; - nativeParent.resize(800, 600); - nativeParent.setAttribute(Qt::WA_NativeWindow); - ClearWidget *child = new ClearWidget(0, 800, 600); - child->setClearColor(0, 1, 0); - child->setParent(&nativeParent); - child->resize(400, 400); - child->move(23, 34); - nativeParent.show(); - QVERIFY(QTest::qWaitForWindowExposed(&nativeParent)); - - QVERIFY(nativeParent.internalWinId()); - QVERIFY(!child->internalWinId()); - - image = child->grabFramebuffer(); - QVERIFY(!image.isNull()); - QCOMPARE(image.width(), child->width()); - QCOMPARE(image.height(), child->height()); - QVERIFY(image.pixel(30, 40) == qRgb(0, 255, 0)); + // NB these tests do not fully verify that native child widgets are fully + // functional since there is no guarantee that the content is composed and + // presented correctly as we can only do verification with + // grabFramebuffer() here which only exercises a part of the pipeline. + + // Install a message handler that looks for some typical warnings from + // QRhi/QOpenGLConext that occur when the RHI-related logic in widgets goes wrong. + oldHandler = qInstallMessageHandler(nativeWindowMessageHandler); + + { + QScopedPointer<ClearWidget> w(new ClearWidget(nullptr, 800, 600)); + w->resize(800, 600); + w->show(); + w->winId(); + QVERIFY(QTest::qWaitForWindowExposed(w.data())); + + QImage image = w->grabFramebuffer(); + QVERIFY(!image.isNull()); + QCOMPARE(image.width(), w->width()); + QCOMPARE(image.height(), w->height()); + QVERIFY(image.pixel(30, 40) == qRgb(255, 0, 0)); + QVERIFY(w->internalWinId()); + } + + // QTBUG-113557: a plain _raster_ QWidget that is a _native_ child in a toplevel + // combined with a RHI-based (non-native) widget (QOpenGLWidget in this case) + // in the same toplevel. + { + QWidget topLevel; + topLevel.resize(800, 600); + + ClearWidget *child = new ClearWidget(&topLevel, 800, 600); + child->setClearColor(1, 0, 0); + child->resize(400, 400); + child->move(23, 34); + + QWidget *raster = new QWidget(&topLevel); + raster->setGeometry(23, 240, 120, 120); + raster->setStyleSheet("QWidget { background-color: yellow; }"); + + raster->winId(); + + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + + // Do not bother checking the output, i.e. if the yellow raster native child + // shows up as it should, but rather rely on the message handler catching the + // qWarnings if they occur. + } + + // Now with the QOpenGLWidget being a native child + { + QWidget topLevel; + topLevel.resize(800, 600); + + ClearWidget *child = new ClearWidget(nullptr, 800, 600); + child->setParent(&topLevel); + + // make it a native child (native window, but not top-level -> no backingstore) + child->winId(); + + child->setClearColor(0, 1, 0); + child->resize(400, 400); + child->move(23, 34); + + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + + QVERIFY(topLevel.internalWinId()); + QVERIFY(child->internalWinId()); + + QImage image = child->grabFramebuffer(); + QVERIFY(!image.isNull()); + QCOMPARE(image.width(), child->width()); + QCOMPARE(image.height(), child->height()); + QVERIFY(image.pixel(30, 40) == qRgb(0, 255, 0)); + } + + // Now the same with WA_NativeWindow instead + { + QWidget topLevel; + topLevel.resize(800, 600); + + ClearWidget *child = new ClearWidget(nullptr, 800, 600); + child->setParent(&topLevel); + + // make it a native child (native window, but not top-level -> no backingstore) + child->setAttribute(Qt::WA_NativeWindow); + + child->setClearColor(0, 1, 0); + child->resize(400, 400); + child->move(23, 34); + + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + + QVERIFY(child->internalWinId()); + + QImage image = child->grabFramebuffer(); + QCOMPARE(image.width(), child->width()); + QCOMPARE(image.height(), child->height()); + QVERIFY(image.pixel(30, 40) == qRgb(0, 255, 0)); + } + + // Now as a child of a native child + { + QWidget topLevel; + topLevel.resize(800, 600); + + QWidget *container = new QWidget(&topLevel); + // make it a native child (native window, but not top-level -> no backingstore) + container->winId(); + + ClearWidget *child = new ClearWidget(nullptr, 800, 600); + // set the parent separately, this is important, see next test case + child->setParent(container); + child->setClearColor(0, 0, 1); + child->resize(400, 400); + child->move(23, 34); + + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + + QVERIFY(topLevel.internalWinId()); + QVERIFY(container->internalWinId()); + QVERIFY(!child->internalWinId()); + + QImage image = child->grabFramebuffer(); + QCOMPARE(image.width(), child->width()); + QCOMPARE(image.height(), child->height()); + QVERIFY(image.pixel(30, 40) == qRgb(0, 0, 255)); + } + + // Again as a child of a native child, but this time specifying the parent + // upon construction, not with an explicit setParent() call afterwards. + { + QWidget topLevel; + topLevel.resize(800, 600); + QWidget *container = new QWidget(&topLevel); + container->winId(); + // parent it right away + ClearWidget *child = new ClearWidget(container, 800, 600); + child->setClearColor(0, 0, 1); + child->resize(400, 400); + child->move(23, 34); + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + QVERIFY(topLevel.internalWinId()); + QVERIFY(container->internalWinId()); + QVERIFY(!child->internalWinId()); + QImage image = child->grabFramebuffer(); + QCOMPARE(image.width(), child->width()); + QCOMPARE(image.height(), child->height()); + QVERIFY(image.pixel(30, 40) == qRgb(0, 0, 255)); + } + + if (oldHandler) { + qInstallMessageHandler(oldHandler); + oldHandler = nullptr; + } } static inline QString msgRgbMismatch(unsigned actual, unsigned expected) @@ -597,50 +788,59 @@ static QPixmap grabWidgetWithoutRepaint(const QWidget *widget, QRect clipArea) bool verifyColor(const QWidget *widget, const QRect &clipArea, const QColor &color, int callerLine) { - for (int t = 0; t < 6; t++) { - const QPixmap pixmap = grabWidgetWithoutRepaint(widget, clipArea); - if (!QTest::qCompare(pixmap.size(), - clipArea.size(), - "pixmap.size()", - "rect.size()", - __FILE__, - callerLine)) - return false; - - - const QImage image = pixmap.toImage(); - QPixmap expectedPixmap(pixmap); /* ensure equal formats */ - expectedPixmap.detach(); - expectedPixmap.fill(color); - + // Create a comparison target image + QPixmap expectedPixmap(grabWidgetWithoutRepaint(widget, clipArea)); /* ensure equal formats */ + expectedPixmap.detach(); + expectedPixmap.fill(color); + const QImage expectedImage = expectedPixmap.toImage(); + + // test image size + QPixmap pixmap; + auto testSize = [&](){ + pixmap = grabWidgetWithoutRepaint(widget, clipArea); + return pixmap.size() == clipArea.size(); + }; + + // test the first pixel's color + uint firstPixel; + auto testPixel = [&](){ + const QImage image = grabWidgetWithoutRepaint(widget, clipArea).toImage(); uint alphaCorrection = image.format() == QImage::Format_RGB32 ? 0xff000000 : 0; - uint firstPixel = image.pixel(0,0) | alphaCorrection; - - // Retry a couple of times. Some window managers have transparency animation, or are - // just slow to render. - if (t < 5) { - if (firstPixel == QColor(color).rgb() - && image == expectedPixmap.toImage()) - return true; - else - QTest::qWait(200); - } else { - if (!QTest::qVerify(firstPixel == QColor(color).rgb(), - "firstPixel == QColor(color).rgb()", - qPrintable(msgRgbMismatch(firstPixel, QColor(color).rgb())), - __FILE__, callerLine)) { - return false; - } - if (!QTest::qVerify(image == expectedPixmap.toImage(), - "image == expectedPixmap.toImage()", - "grabbed pixmap differs from expected pixmap", - __FILE__, callerLine)) { - return false; - } - } + firstPixel = image.pixel(0,0) | alphaCorrection; + return firstPixel == QColor(color).rgb(); + }; + + // test the rendered image + QImage image; + auto testImage = [&](){ + image = grabWidgetWithoutRepaint(widget, clipArea).toImage(); + return image == expectedImage; + }; + + // Perform checks and make test case fail if unsuccessful + if (!QTest::qWaitFor(testSize)) + return QTest::qCompare(pixmap.size(), + clipArea.size(), + "pixmap.size()", + "rect.size()", + __FILE__, + callerLine); + + if (!QTest::qWaitFor(testPixel)) { + return QTest::qVerify(firstPixel == QColor(color).rgb(), + "firstPixel == QColor(color).rgb()", + qPrintable(msgRgbMismatch(firstPixel, QColor(color).rgb())), + __FILE__, callerLine); + } + + if (!QTest::qWaitFor(testImage)) { + return QTest::qVerify(image == expectedImage, + "image == expectedPixmap.toImage()", + "grabbed pixmap differs from expected pixmap", + __FILE__, callerLine); } - return false; + return true; } void tst_QOpenGLWidget::stackWidgetOpaqueChildIsVisible() @@ -673,11 +873,12 @@ void tst_QOpenGLWidget::stackWidgetOpaqueChildIsVisible() stack.resize(dimensionSize, dimensionSize); stack.show(); QVERIFY(QTest::qWaitForWindowExposed(&stack)); + stack.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&stack)); // Switch to the QOpenGLWidget. stack.setCurrentIndex(1); - QTRY_COMPARE(clearWidget->m_paintCalled, true); + QTRY_VERIFY(clearWidget->m_paintCalled); // Resize the tested region to be half size in the middle, because some OSes make the widget // have rounded corners (e.g. OSX), and the grabbed window pixmap will not coincide perfectly @@ -785,10 +986,34 @@ void tst_QOpenGLWidget::paintWhileHidden() // on-screen at the point when update() is called. w->setVisible(false); - w->m_paintCalled = 0; + w->m_paintCalled = false; w->update(); w->setVisible(true); - QTRY_VERIFY(w->m_paintCalled > 0); + QTRY_VERIFY(w->m_paintCalled); +} + +void tst_QOpenGLWidget::widgetWindowColorFormat_data() +{ + QTest::addColumn<bool>("translucent"); + QTest::newRow("Translucent background disabled") << false; + QTest::newRow("Translucent background enabled") << true; +} + +void tst_QOpenGLWidget::widgetWindowColorFormat() +{ + QFETCH(bool, translucent); + + QOpenGLWidget w; + w.setAttribute(Qt::WA_TranslucentBackground, translucent); + w.setWindowTitle(QLatin1String(QTest::currentTestFunction())); + w.setFixedSize(16, 16); + w.show(); + QVERIFY(QTest::qWaitForWindowExposed(&w)); + + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + QCOMPARE(w.format().redBufferSize(), ctx->format().redBufferSize()); + QCOMPARE(w.format().greenBufferSize(), ctx->format().greenBufferSize()); + QCOMPARE(w.format().blueBufferSize(), ctx->format().blueBufferSize()); } class StaticTextPainterWidget : public QOpenGLWidget |