diff options
author | Alexandru Croitor <alexandru.croitor@qt.io> | 2019-09-04 14:33:40 +0200 |
---|---|---|
committer | Alexandru Croitor <alexandru.croitor@qt.io> | 2019-09-04 14:33:40 +0200 |
commit | f255b1e8e297e7e1363921580007145cff574e0d (patch) | |
tree | 9a799be282e6c0d6544d9f8c872073f83e6c0475 /src/testlib | |
parent | 7e8705f6632428a8d9a937ab5fe087999347b3dd (diff) | |
parent | bf8fcab8bb92ff534c5cec048d6dbebb3b73a348 (diff) |
Merge remote-tracking branch 'origin/dev' into wip/qt6
Change-Id: I54741635460bb2d8f3fd0be535ee1968d6c442bb
Diffstat (limited to 'src/testlib')
-rw-r--r-- | src/testlib/doc/snippets/code/doc_src_qsignalspy.cpp | 48 | ||||
-rw-r--r-- | src/testlib/doc/src/qttestlib-manual.qdoc | 5 | ||||
-rw-r--r-- | src/testlib/qsignalspy.h | 71 | ||||
-rw-r--r-- | src/testlib/qsignalspy.qdoc | 22 | ||||
-rw-r--r-- | src/testlib/qtest.h | 32 | ||||
-rw-r--r-- | src/testlib/qtestcase.h | 2 |
6 files changed, 158 insertions, 22 deletions
diff --git a/src/testlib/doc/snippets/code/doc_src_qsignalspy.cpp b/src/testlib/doc/snippets/code/doc_src_qsignalspy.cpp index a4513a55a9..37aba2715b 100644 --- a/src/testlib/doc/snippets/code/doc_src_qsignalspy.cpp +++ b/src/testlib/doc/snippets/code/doc_src_qsignalspy.cpp @@ -98,3 +98,51 @@ QVERIFY(spy.wait(1000)); QSignalSpy spy(myPushButton, &QPushButton::clicked); //! [6] +//! [7] +QObject object; +auto mo = object.metaObject(); +auto signalIndex = mo->indexOfSignal("objectNameChanged(QString)"); +auto signal = mo->method(signalIndex); + +QSignalSpy spy(&object, signal); +object.setObjectName("A new object name"); +QCOMPARE(spy.count(), 1); +//! [7] + +//! [8] +void tst_QWindow::writeMinMaxDimensionalProps_data() + QTest::addColumn<int>("propertyIndex"); + + // Collect all relevant properties + static const auto mo = QWindow::staticMetaObject; + for (int i = mo.propertyOffset(); i < mo.propertyCount(); ++i) { + auto property = mo.property(i); + + // ...that have type int + if (property.type() == QVariant::Int) { + static const QRegularExpression re("^minimum|maximum"); + const auto name = property.name(); + + // ...and start with "minimum" or "maximum" + if (re.match(name).hasMatch()) { + QTest::addRow("%s", name) << i; + } + } + } +} + +void tst_QWindow::writeMinMaxDimensionalProps() +{ + QFETCH(int, propertyIndex); + + auto property = QWindow::staticMetaObject.property(propertyIndex); + QVERIFY(property.isWritable()); + QVERIFY(property.hasNotifySignal()); + + QWindow window; + QSignalSpy spy(&window, property.notifySignal()); + + QVERIFY(property.write(&window, 42)); + QCOMPARE(spy.count(), 1); +} +//! [8] diff --git a/src/testlib/doc/src/qttestlib-manual.qdoc b/src/testlib/doc/src/qttestlib-manual.qdoc index 71b4541313..65836d0706 100644 --- a/src/testlib/doc/src/qttestlib-manual.qdoc +++ b/src/testlib/doc/src/qttestlib-manual.qdoc @@ -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) diff --git a/src/testlib/qsignalspy.h b/src/testlib/qsignalspy.h index 0285080662..dc0c58044f 100644 --- a/src/testlib/qsignalspy.h +++ b/src/testlib/qsignalspy.h @@ -59,11 +59,8 @@ public: explicit QSignalSpy(const QObject *obj, const char *aSignal) : m_waiting(false) { - static const int memberOffset = QObject::staticMetaObject.methodCount(); - if (!obj) { - qWarning("QSignalSpy: Cannot spy on a null object"); + if (!isObjectValid(obj)) return; - } if (!aSignal) { qWarning("QSignalSpy: Null signal name is not valid"); @@ -83,11 +80,9 @@ public: return; } - if (!QMetaObject::connect(obj, sigIndex, this, memberOffset, - Qt::DirectConnection, nullptr)) { - qWarning("QSignalSpy: QMetaObject::connect returned false. Unable to connect."); + if (!connectToSignal(obj, sigIndex)) return; - } + sig = ba; initArgs(mo->method(sigIndex), obj); } @@ -100,11 +95,8 @@ public: QSignalSpy(const typename QtPrivate::FunctionPointer<Func>::Object *obj, Func signal0) : m_waiting(false) { - static const int memberOffset = QObject::staticMetaObject.methodCount(); - if (!obj) { - qWarning("QSignalSpy: Cannot spy on a null object"); + if (!isObjectValid(obj)) return; - } if (!signal0) { qWarning("QSignalSpy: Null signal name is not valid"); @@ -114,23 +106,28 @@ public: const QMetaObject * const mo = obj->metaObject(); const QMetaMethod signalMetaMethod = QMetaMethod::fromSignal(signal0); const int sigIndex = signalMetaMethod.methodIndex(); - if (!signalMetaMethod.isValid() || - signalMetaMethod.methodType() != QMetaMethod::Signal) { - qWarning("QSignalSpy: Not a valid signal: '%s'", - signalMetaMethod.methodSignature().constData()); + + if (!isSignalMetaMethodValid(signalMetaMethod)) return; - } - if (!QMetaObject::connect(obj, sigIndex, this, memberOffset, - Qt::DirectConnection, nullptr)) { - qWarning("QSignalSpy: QMetaObject::connect returned false. Unable to connect."); + if (!connectToSignal(obj, sigIndex)) return; - } + sig = signalMetaMethod.methodSignature(); initArgs(mo->method(sigIndex), obj); } #endif // Q_CLANG_QDOC + QSignalSpy(const QObject *obj, const QMetaMethod &signal) + : m_waiting(false) + { + if (isObjectValid(obj) && isSignalMetaMethodValid(signal) && + connectToSignal(obj, signal.methodIndex())) { + sig = signal.methodSignature(); + initArgs(signal, obj); + } + } + inline bool isValid() const { return !sig.isEmpty(); } inline QByteArray signal() const { return sig; } @@ -160,6 +157,38 @@ public: } private: + bool connectToSignal(const QObject *sender, int sigIndex) + { + static const int memberOffset = QObject::staticMetaObject.methodCount(); + const bool connected = QMetaObject::connect( + sender, sigIndex, this, memberOffset, Qt::DirectConnection, nullptr); + + if (!connected) + qWarning("QSignalSpy: QMetaObject::connect returned false. Unable to connect."); + + return connected; + } + + static bool isSignalMetaMethodValid(const QMetaMethod &signal) + { + const bool valid = signal.isValid() && signal.methodType() == QMetaMethod::Signal; + + if (!valid) + qWarning("QSignalSpy: Not a valid signal: '%s'", signal.methodSignature().constData()); + + return valid; + } + + static bool isObjectValid(const QObject *object) + { + const bool valid = !!object; + + if (!valid) + qWarning("QSignalSpy: Cannot spy on a null object"); + + return valid; + } + void initArgs(const QMetaMethod &member, const QObject *obj) { args.reserve(member.parameterCount()); diff --git a/src/testlib/qsignalspy.qdoc b/src/testlib/qsignalspy.qdoc index 3352307d69..5ea6bc5dc7 100644 --- a/src/testlib/qsignalspy.qdoc +++ b/src/testlib/qsignalspy.qdoc @@ -86,6 +86,28 @@ \snippet code/doc_src_qsignalspy.cpp 6 */ +/*! \fn QSignalSpy(const QObject *obj, const QMetaMethod &signal) + \since 5.14 + + Constructs a new QSignalSpy that listens for emissions of the \a signal + from the QObject \a object. If QSignalSpy is not able to listen for a + valid signal (for example, because \a object is \nullptr or \a signal does + not denote a valid signal of \a object), an explanatory warning message + will be output using qWarning() and subsequent calls to \c isValid() will + return false. + + This constructor is convenient to use when Qt's meta-object system is + heavily used in a test. + + Basic usage example: + \snippet code/doc_src_qsignalspy.cpp 7 + + Imagine we need to check whether all properties of the QWindow class + that represent minimum and maximum dimensions are properly writable. + The following example demonstrates one of the approaches: + \snippet code/doc_src_qsignalspy.cpp 8 +*/ + /*! \fn QSignalSpy::isValid() const Returns \c true if the signal spy listens to a valid signal, otherwise false. diff --git a/src/testlib/qtest.h b/src/testlib/qtest.h index cdf0800371..27fe08e8f4 100644 --- a/src/testlib/qtest.h +++ b/src/testlib/qtest.h @@ -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; \ diff --git a/src/testlib/qtestcase.h b/src/testlib/qtestcase.h index e61e450e98..e1518708e8 100644 --- a/src/testlib/qtestcase.h +++ b/src/testlib/qtestcase.h @@ -153,7 +153,7 @@ do {\ #define QTRY_IMPL(expr, timeout)\ const int qt_test_step = timeout < 350 ? timeout / 7 + 1 : 50; \ const int qt_test_timeoutValue = timeout; \ - QTRY_LOOP_IMPL((expr), qt_test_timeoutValue, qt_test_step); \ + { QTRY_LOOP_IMPL((expr), qt_test_timeoutValue, qt_test_step); } \ QTRY_TIMEOUT_DEBUG_IMPL((expr), qt_test_timeoutValue, qt_test_step)\ // Will try to wait for the expression to become true while allowing event processing |