summaryrefslogtreecommitdiffstats
path: root/src/testlib
diff options
context:
space:
mode:
authorAlexandru Croitor <alexandru.croitor@qt.io>2019-09-04 14:33:40 +0200
committerAlexandru Croitor <alexandru.croitor@qt.io>2019-09-04 14:33:40 +0200
commitf255b1e8e297e7e1363921580007145cff574e0d (patch)
tree9a799be282e6c0d6544d9f8c872073f83e6c0475 /src/testlib
parent7e8705f6632428a8d9a937ab5fe087999347b3dd (diff)
parentbf8fcab8bb92ff534c5cec048d6dbebb3b73a348 (diff)
Merge remote-tracking branch 'origin/dev' into wip/qt6
Diffstat (limited to 'src/testlib')
-rw-r--r--src/testlib/doc/snippets/code/doc_src_qsignalspy.cpp48
-rw-r--r--src/testlib/doc/src/qttestlib-manual.qdoc5
-rw-r--r--src/testlib/qsignalspy.h71
-rw-r--r--src/testlib/qsignalspy.qdoc22
-rw-r--r--src/testlib/qtest.h32
-rw-r--r--src/testlib/qtestcase.h2
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