Make tst_qwidget pass on High-DPI screens (Windows)

- Move the fuzz check introduced by
  63090627220a6209652d236cf991305fbeb188b8 to a shared header for
  reuse. Use it in in more places to account for rounding errors
  introduced by odd window frame sizes when scaling is active.

- Use the test widget size to ensure windows do not violate the
  minimum decorated window size on Windows when scaling is inactive
  on large monitors.

Task-number: QTBUG-46615
Change-Id: Icf803a4bc2c275eadb8f98e60b08e39b2ebebedd
Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
This commit is contained in:
Friedemann Kleint 2019-05-09 13:44:12 +02:00
parent 085d1335d1
commit 67c569add0
2 changed files with 143 additions and 34 deletions

109
tests/auto/shared/highdpi.h Normal file
View File

@ -0,0 +1,109 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** 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$
**
****************************************************************************/
#ifndef HIGHDPI_H
#define HIGHDPI_H
#include <QtCore/qbytearray.h>
#include <QtCore/qpoint.h>
#include <QtCore/qrect.h>
#include <QtCore/qsize.h>
// Helpers for comparing geometries a that may go through scaling in the
// platform plugin with fuzz (pass rounded-down device pixel ratios or
// scaling factors). Error message for use with QVERIFY2() are also provided.
class HighDpi
{
public:
HighDpi() = delete;
HighDpi(const HighDpi &) = delete;
HighDpi &operator=(const HighDpi &) = delete;
HighDpi(HighDpi &&) = delete;
HighDpi &operator=(HighDpi &&) = delete;
~HighDpi() = delete;
static int manhattanDelta(const QPoint &p1, const QPoint p2)
{
return (p1 - p2).manhattanLength();
}
static bool fuzzyCompare(const QPoint &p1, const QPoint p2, int fuzz)
{
return manhattanDelta(p1, p2) <= fuzz;
}
static QByteArray msgPointMismatch(const QPoint &p1, const QPoint p2)
{
return QByteArray::number(p1.x()) + ',' + QByteArray::number(p1.y())
+ " != " + QByteArray::number(p2.x()) + ',' + QByteArray::number(p2.y())
+ ", manhattanLength=" + QByteArray::number(manhattanDelta(p1, p2));
}
// Compare a size that may go through scaling in the platform plugin with fuzz.
static inline int manhattanDelta(const QSize &s1, const QSize &s2)
{
return qAbs(s1.width() - s2.width()) + qAbs(s1.height() - s2.height());
}
static inline bool fuzzyCompare(const QSize &s1, const QSize &s2, int fuzz)
{
return manhattanDelta(s1, s2) <= fuzz;
}
static QByteArray msgSizeMismatch(const QSize &s1, const QSize &s2)
{
return QByteArray::number(s1.width()) + 'x' + QByteArray::number(s1.height())
+ " != " + QByteArray::number(s2.width()) + 'x' + QByteArray::number(s2.height())
+ ", manhattanLength=" + QByteArray::number(manhattanDelta(s1, s2));
}
// Compare a geometry that may go through scaling in the platform plugin with fuzz.
static inline bool fuzzyCompare(const QRect &r1, const QRect &r2, int fuzz)
{
return manhattanDelta(r1.topLeft(), r2.topLeft()) <= fuzz
&& manhattanDelta(r1.size(), r2.size()) <= fuzz;
}
static QByteArray msgRectMismatch(const QRect &r1, const QRect &r2)
{
return formatRect(r1) + " != " + formatRect(r2);
}
private:
static QByteArray formatRect(const QRect &r)
{
return QByteArray::number(r.width()) + 'x' + QByteArray::number(r.height())
+ (r.left() < 0 ? '-' : '+') + QByteArray::number(r.left())
+ (r.top() < 0 ? '-' : '+') + QByteArray::number(r.top());
}
};
#endif // HIGHDPI_H

View File

@ -26,6 +26,7 @@
**
****************************************************************************/
#include "../../../shared/highdpi.h"
#include <qboxlayout.h>
#include <qapplication.h>
@ -140,19 +141,6 @@ static QByteArray msgComparisonFailed(T v1, const char *op, T v2)
return s.toLocal8Bit();
}
// Compare a window position that may go through scaling in the platform plugin with fuzz.
static inline bool qFuzzyCompareWindowPosition(const QPoint &p1, const QPoint p2, int fuzz)
{
return (p1 - p2).manhattanLength() <= fuzz;
}
static QString msgPointMismatch(const QPoint &p1, const QPoint p2)
{
QString result;
QDebug(&result) << p1 << "!=" << p2 << ", manhattanLength=" << (p1 - p2).manhattanLength();
return result;
}
class tst_QWidget : public QObject
{
Q_OBJECT
@ -416,6 +404,7 @@ private:
QPoint m_safeCursorPos;
const bool m_windowsAnimationsEnabled;
QTouchDevice *m_touchScreen;
const int m_fuzz;
};
bool tst_QWidget::ensureScreenSize(int width, int height)
@ -574,6 +563,7 @@ tst_QWidget::tst_QWidget()
, m_safeCursorPos(0, 0)
, m_windowsAnimationsEnabled(windowsAnimationsEnabled())
, m_touchScreen(QTest::createTouchDevice())
, m_fuzz(int(QGuiApplication::primaryScreen()->devicePixelRatio()))
{
if (m_windowsAnimationsEnabled) // Disable animations which can interfere with screen grabbing in moveChild(), showAndMoveChild()
setWindowsAnimationsEnabled(false);
@ -2047,10 +2037,9 @@ void tst_QWidget::windowState()
widget1.setWindowState(widget1.windowState() ^ Qt::WindowMaximized);
QTest::qWait(100);
const int fuzz = int(QHighDpiScaling::factor(widget1.windowHandle()));
QVERIFY(!(widget1.windowState() & Qt::WindowMaximized));
QTRY_VERIFY2(qFuzzyCompareWindowPosition(widget1.pos(), pos, fuzz),
qPrintable(msgPointMismatch(widget1.pos(), pos)));
QTRY_VERIFY2(HighDpi::fuzzyCompare(widget1.pos(), pos, m_fuzz),
qPrintable(HighDpi::msgPointMismatch(widget1.pos(), pos)));
QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowNoState);
widget1.setWindowState(Qt::WindowMinimized);
@ -2071,8 +2060,8 @@ void tst_QWidget::windowState()
widget1.setWindowState(widget1.windowState() ^ Qt::WindowMaximized);
QTest::qWait(100);
QVERIFY(!(widget1.windowState() & (Qt::WindowMinimized|Qt::WindowMaximized)));
QTRY_VERIFY2(qFuzzyCompareWindowPosition(widget1.pos(), pos, fuzz),
qPrintable(msgPointMismatch(widget1.pos(), pos)));
QTRY_VERIFY2(HighDpi::fuzzyCompare(widget1.pos(), pos, m_fuzz),
qPrintable(HighDpi::msgPointMismatch(widget1.pos(), pos)));
QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowNoState);
widget1.setWindowState(Qt::WindowFullScreen);
@ -2093,8 +2082,8 @@ void tst_QWidget::windowState()
widget1.setWindowState(Qt::WindowNoState);
QTest::qWait(100);
VERIFY_STATE(Qt::WindowNoState);
QTRY_VERIFY2(qFuzzyCompareWindowPosition(widget1.pos(), pos, fuzz),
qPrintable(msgPointMismatch(widget1.pos(), pos)));
QTRY_VERIFY2(HighDpi::fuzzyCompare(widget1.pos(), pos, m_fuzz),
qPrintable(HighDpi::msgPointMismatch(widget1.pos(), pos)));
QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowNoState);
widget1.setWindowState(Qt::WindowFullScreen);
@ -2127,8 +2116,8 @@ void tst_QWidget::windowState()
QVERIFY(!(widget1.windowState() & stateMask));
QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowNoState);
QTRY_VERIFY2(qFuzzyCompareWindowPosition(widget1.pos(), pos, fuzz),
qPrintable(msgPointMismatch(widget1.pos(), pos)));
QTRY_VERIFY2(HighDpi::fuzzyCompare(widget1.pos(), pos, m_fuzz),
qPrintable(HighDpi::msgPointMismatch(widget1.pos(), pos)));
QTRY_COMPARE(widget1.size(), size);
}
@ -2321,7 +2310,7 @@ void tst_QWidget::resizeEvent()
{
QWidget wParent;
wParent.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
wParent.resize(200, 200);
wParent.resize(m_testWidgetSize);
ResizeWidget wChild(&wParent);
wParent.show();
QVERIFY(QTest::qWaitForWindowExposed(&wParent));
@ -2339,7 +2328,7 @@ void tst_QWidget::resizeEvent()
{
ResizeWidget wTopLevel;
wTopLevel.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
wTopLevel.resize(200, 200);
wTopLevel.resize(m_testWidgetSize);
wTopLevel.show();
QVERIFY(QTest::qWaitForWindowExposed(&wTopLevel));
if (m_platform == QStringLiteral("winrt"))
@ -2377,17 +2366,20 @@ void tst_QWidget::showMinimized()
#ifdef Q_OS_WINRT
QEXPECT_FAIL("", "Winrt does not support move and resize", Abort);
#endif
QCOMPARE(plain.pos(), pos);
QVERIFY2(HighDpi::fuzzyCompare(plain.pos(), pos, m_fuzz),
qPrintable(HighDpi::msgPointMismatch(plain.pos(), pos)));
plain.showNormal();
QVERIFY(!plain.isMinimized());
QVERIFY(plain.isVisible());
QCOMPARE(plain.pos(), pos);
QVERIFY2(HighDpi::fuzzyCompare(plain.pos(), pos, m_fuzz),
qPrintable(HighDpi::msgPointMismatch(plain.pos(), pos)));
plain.showMinimized();
QVERIFY(plain.isMinimized());
QVERIFY(plain.isVisible());
QCOMPARE(plain.pos(), pos);
QVERIFY2(HighDpi::fuzzyCompare(plain.pos(), pos, m_fuzz),
qPrintable(HighDpi::msgPointMismatch(plain.pos(), pos)));
plain.hide();
QVERIFY(plain.isMinimized());
@ -2779,7 +2771,9 @@ void tst_QWidget::setGeometry()
tlw.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
QWidget child(&tlw);
QRect tr(100,100,200,200);
const QPoint topLeft = QGuiApplication::primaryScreen()->availableGeometry().topLeft();
const QSize initialSize = 2 * m_testWidgetSize;
QRect tr(topLeft + QPoint(100,100), initialSize);
QRect cr(50,50,50,50);
tlw.setGeometry(tr);
child.setGeometry(cr);
@ -2790,8 +2784,7 @@ void tst_QWidget::setGeometry()
QCOMPARE(child.geometry(), cr);
tlw.setParent(nullptr, Qt::Window|Qt::FramelessWindowHint);
tr = QRect(0,0,100,100);
tr.moveTopLeft(QGuiApplication::primaryScreen()->availableGeometry().topLeft());
tr = QRect(topLeft, initialSize / 2);
tlw.setGeometry(tr);
QCOMPARE(tlw.geometry(), tr);
tlw.showNormal();
@ -3263,7 +3256,8 @@ void tst_QWidget::saveRestoreGeometry()
if (m_platform == QStringLiteral("winrt"))
QEXPECT_FAIL("", "WinRT does not support move/resize", Abort);
QTRY_COMPARE(widget.pos(), position);
QTRY_VERIFY2(HighDpi::fuzzyCompare(widget.pos(), position, m_fuzz),
qPrintable(HighDpi::msgPointMismatch(widget.pos(), position)));
QCOMPARE(widget.size(), size);
savedGeometry = widget.saveGeometry();
}
@ -3291,10 +3285,12 @@ void tst_QWidget::saveRestoreGeometry()
QVERIFY(QTest::qWaitForWindowExposed(&widget));
QApplication::processEvents();
QTRY_COMPARE(widget.pos(), position);
QVERIFY2(HighDpi::fuzzyCompare(widget.pos(), position, m_fuzz),
qPrintable(HighDpi::msgPointMismatch(widget.pos(), position)));
QCOMPARE(widget.size(), size);
widget.show();
QCOMPARE(widget.pos(), position);
QVERIFY2(HighDpi::fuzzyCompare(widget.pos(), position, m_fuzz),
qPrintable(HighDpi::msgPointMismatch(widget.pos(), position)));
QCOMPARE(widget.size(), size);
}
@ -3408,6 +3404,9 @@ void tst_QWidget::restoreVersion1Geometry()
QFETCH(QSize, expectedSize);
QFETCH(QRect, expectedNormalGeometry);
if (m_platform == QLatin1String("windows") && QGuiApplication::primaryScreen()->geometry().width() > 2000)
QSKIP("Skipping due to minimum decorated window size on Windows");
// WindowActive is uninteresting for this test
const Qt::WindowStates WindowStateMask = Qt::WindowFullScreen | Qt::WindowMaximized | Qt::WindowMinimized;
@ -4988,7 +4987,8 @@ void tst_QWidget::windowMoveResize()
widget.showNormal();
QTest::qWait(10);
QTRY_COMPARE(widget.pos(), rect.topLeft());
QTRY_VERIFY2(HighDpi::fuzzyCompare(widget.pos(), rect.topLeft(), m_fuzz),
qPrintable(HighDpi::msgPointMismatch(widget.pos(), rect.topLeft())));
// Windows: Minimum size of decorated windows.
const bool expectResizeFail = (!windowFlags && (rect.width() < 160 || rect.height() < 40))
&& m_platform == QStringLiteral("windows");