QTestLib: Introduce initMain() to run in main before qApp exists

When running Qt autotests on a developer machine with a high
resolution, failures occur due to either some widget becoming too
small, some rounding fuzz appearing when Qt High DPI scaling is active,
or some test taking screenshots failing to deal with device pixel
ratios != 1 in the obtained pixmaps.

It is not feasible to adapt all tests to pass on high resolution
monitors in both modes (Qt High DPI scaling enabled/disabled). It
should be possible to specify the High DPI setting per test.

Previously, it was not possible to set the Qt High DPI scaling
attributes since they must be applied before QApplication
instantiation.

Enable this by checking for the presence of a static void initMain()
function on the test object and invoking it before QApplication
instantiation.

Prototypically use it in tst_qtimer and to turn off High DPI scaling for
tst_QGL.

[ChangeLog][QtTestLib] It is now possible to perform static
initialization before QApplication instantiation by implementing a
initMain() function in the test class.

Change-Id: Idec0134b189710a14c41a451fa8445bc0c5b1cf3
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Reviewed-by: Paul Wicking <paul.wicking@qt.io>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
bb10
Friedemann Kleint 2019-07-30 10:16:53 +02:00
parent 02ae522f54
commit ecb6327762
7 changed files with 124 additions and 9 deletions

View File

@ -107,6 +107,11 @@
Example:
\snippet code/doc_src_qtestlib.cpp 0
Finally, if the test class has a static public \c{void initMain()} method,
it is called by the QTEST_MAIN macros before the QApplication object
is instantiated. For example, this allows for setting application
attributes like Qt::AA_DisableHighDpiScaling. This was added in 5.14.
For more examples, refer to the \l{Qt Test Tutorial}.
\if !defined(qtforpython)

View File

@ -379,8 +379,36 @@ inline bool qCompare(quint32 const &t1, quint64 const &t2, const char *actual,
{
return qCompare(static_cast<quint64>(t1), t2, actual, expected, file, line);
}
namespace Internal {
template <typename T>
class HasInitMain // SFINAE test for the presence of initMain()
{
private:
using YesType = char[1];
using NoType = char[2];
template <typename C> static YesType& test( decltype(&C::initMain) ) ;
template <typename C> static NoType& test(...);
public:
enum { value = sizeof(test<T>(nullptr)) == sizeof(YesType) };
};
template<typename T>
typename std::enable_if<HasInitMain<T>::value, void>::type callInitMain()
{
T::initMain();
}
template<typename T>
typename std::enable_if<!HasInitMain<T>::value, void>::type callInitMain()
{
}
} // namespace Internal
} // namespace QTest
QT_END_NAMESPACE
#ifdef QT_TESTCASE_BUILDDIR
@ -441,6 +469,7 @@ int main(int argc, char *argv[]) \
#define QTEST_MAIN_IMPL(TestObject) \
TESTLIB_SELFCOVERAGE_START(#TestObject) \
QT_PREPEND_NAMESPACE(QTest::Internal::callInitMain)<TestObject>(); \
QApplication app(argc, argv); \
app.setAttribute(Qt::AA_Use96Dpi, true); \
QTEST_DISABLE_KEYPAD_NAVIGATION \
@ -454,6 +483,7 @@ int main(int argc, char *argv[]) \
#define QTEST_MAIN_IMPL(TestObject) \
TESTLIB_SELFCOVERAGE_START(#TestObject) \
QT_PREPEND_NAMESPACE(QTest::Internal::callInitMain)<TestObject>(); \
QGuiApplication app(argc, argv); \
app.setAttribute(Qt::AA_Use96Dpi, true); \
TestObject tc; \
@ -464,6 +494,7 @@ int main(int argc, char *argv[]) \
#define QTEST_MAIN_IMPL(TestObject) \
TESTLIB_SELFCOVERAGE_START(#TestObject) \
QT_PREPEND_NAMESPACE(QTest::Internal::callInitMain)<TestObject>(); \
QCoreApplication app(argc, argv); \
app.setAttribute(Qt::AA_Use96Dpi, true); \
TestObject tc; \
@ -482,6 +513,7 @@ int main(int argc, char *argv[]) \
int main(int argc, char *argv[]) \
{ \
TESTLIB_SELFCOVERAGE_START(#TestObject) \
QT_PREPEND_NAMESPACE(QTest::Internal::callInitMain)<TestObject>(); \
QCoreApplication app(argc, argv); \
app.setAttribute(Qt::AA_Use96Dpi, true); \
TestObject tc; \

View File

@ -47,7 +47,11 @@
class tst_QTimer : public QObject
{
Q_OBJECT
public:
static void initMain();
private slots:
void cleanupTestCase();
void zeroTimer();
void singleShotTimeout();
void timeout();
@ -1138,22 +1142,27 @@ struct StaticSingleShotUser
}
};
// NOTE: to prevent any static initialization order fiasco, we implement
// initMain() to instantiate staticSingleShotUser before qApp
static StaticSingleShotUser *s_staticSingleShotUser = nullptr;
void tst_QTimer::initMain()
{
s_staticSingleShotUser = new StaticSingleShotUser;
}
void tst_QTimer::cleanupTestCase()
{
delete s_staticSingleShotUser;
}
void tst_QTimer::singleShot_static()
{
QCoreApplication::processEvents();
QCOMPARE(s_staticSingleShotUser->helper.calls, s_staticSingleShotUser->calls());
}
// NOTE: to prevent any static initialization order fiasco, we handle QTEST_MAIN
// ourselves, but instantiate the staticSingleShotUser before qApp
int main(int argc, char *argv[])
{
StaticSingleShotUser staticSingleShotUser;
s_staticSingleShotUser = &staticSingleShotUser;
QTEST_MAIN_IMPL(tst_QTimer)
}
QTEST_MAIN(tst_QTimer)
#include "tst_qtimer.moc"

View File

@ -60,6 +60,8 @@ public:
tst_QGL();
virtual ~tst_QGL();
static void initMain();
private slots:
void initTestCase();
void getSetCheck();
@ -101,6 +103,11 @@ tst_QGL::~tst_QGL()
{
}
void tst_QGL::initMain()
{
QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
}
void tst_QGL::initTestCase()
{
QGLWidget glWidget;

View File

@ -0,0 +1,5 @@
CONFIG += testcase
SOURCES += tst_initmain.cpp
QT = core testlib
TARGET = tst_initmain

View File

@ -0,0 +1,56 @@
/****************************************************************************
**
** 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$
**
****************************************************************************/
#include <QtCore/QCoreApplication>
#include <QtTest/QtTest>
class tst_InitMain : public QObject
{
Q_OBJECT
public:
static void initMain() { m_initMainCalled = true; }
private slots:
void testcase();
private:
static bool m_initMainCalled;
};
bool tst_InitMain::m_initMainCalled = false;
void tst_InitMain::testcase()
{
QVERIFY(m_initMainCalled);
}
QTEST_MAIN(tst_InitMain)
#include "tst_initmain.moc"

View File

@ -1,5 +1,6 @@
TEMPLATE = subdirs
SUBDIRS = \
initmain \
outformat \
qsignalspy \
selftests \