summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/io/qprocess_p.h3
-rw-r--r--src/corelib/io/qprocess_win.cpp18
-rw-r--r--src/corelib/io/qwindowspipereader.cpp11
-rw-r--r--src/corelib/io/qwindowspipereader_p.h1
-rw-r--r--src/corelib/io/qwindowspipewriter.cpp14
-rw-r--r--src/corelib/io/qwindowspipewriter_p.h2
-rw-r--r--src/corelib/serialization/qxmlstream.cpp5
-rw-r--r--src/network/socket/qlocalsocket_p.h6
-rw-r--r--src/network/socket/qlocalsocket_win.cpp33
-rw-r--r--src/testlib/CMakeLists.txt1
-rw-r--r--src/testlib/qpropertytesthelper_p.h304
-rw-r--r--tests/auto/corelib/io/qprocess/tst_qprocess.cpp4
12 files changed, 369 insertions, 33 deletions
diff --git a/src/corelib/io/qprocess_p.h b/src/corelib/io/qprocess_p.h
index 73db9423e6..f1572055f4 100644
--- a/src/corelib/io/qprocess_p.h
+++ b/src/corelib/io/qprocess_p.h
@@ -294,6 +294,9 @@ public:
// private slots
bool _q_canReadStandardOutput();
bool _q_canReadStandardError();
+#ifdef Q_OS_WIN
+ void _q_bytesWritten(qint64 bytes);
+#endif
bool _q_canWrite();
bool _q_startupNotification();
void _q_processDied();
diff --git a/src/corelib/io/qprocess_win.cpp b/src/corelib/io/qprocess_win.cpp
index d0e57b51fa..388e163317 100644
--- a/src/corelib/io/qprocess_win.cpp
+++ b/src/corelib/io/qprocess_win.cpp
@@ -49,6 +49,7 @@
#include <qfileinfo.h>
#include <qrandom.h>
#include <qwineventnotifier.h>
+#include <qscopedvaluerollback.h>
#include <private/qsystemlibrary_p.h>
#include <private/qthread_p.h>
#include <qdebug.h>
@@ -832,16 +833,25 @@ qint64 QProcessPrivate::pipeWriterBytesToWrite() const
return stdinChannel.writer ? stdinChannel.writer->bytesToWrite() : qint64(0);
}
+void QProcessPrivate::_q_bytesWritten(qint64 bytes)
+{
+ Q_Q(QProcess);
+
+ if (!emittedBytesWritten) {
+ QScopedValueRollback<bool> guard(emittedBytesWritten, true);
+ emit q->bytesWritten(bytes);
+ }
+ _q_canWrite();
+}
+
bool QProcessPrivate::writeToStdin()
{
Q_Q(QProcess);
if (!stdinChannel.writer) {
stdinChannel.writer = new QWindowsPipeWriter(stdinChannel.pipe[1], q);
- QObject::connect(stdinChannel.writer, &QWindowsPipeWriter::bytesWritten,
- q, &QProcess::bytesWritten);
- QObjectPrivate::connect(stdinChannel.writer, &QWindowsPipeWriter::canWrite,
- this, &QProcessPrivate::_q_canWrite);
+ QObjectPrivate::connect(stdinChannel.writer, &QWindowsPipeWriter::bytesWritten,
+ this, &QProcessPrivate::_q_bytesWritten);
} else {
if (stdinChannel.writer->isWriteOperationActive())
return true;
diff --git a/src/corelib/io/qwindowspipereader.cpp b/src/corelib/io/qwindowspipereader.cpp
index b8ea89b8e3..9030c3aaa1 100644
--- a/src/corelib/io/qwindowspipereader.cpp
+++ b/src/corelib/io/qwindowspipereader.cpp
@@ -39,7 +39,6 @@
****************************************************************************/
#include "qwindowspipereader_p.h"
-#include <qscopedvaluerollback.h>
#include <qcoreapplication.h>
#include <QMutexLocker>
@@ -61,8 +60,7 @@ QWindowsPipeReader::QWindowsPipeReader(QObject *parent)
readSequenceStarted(false),
pipeBroken(false),
readyReadPending(false),
- winEventActPosted(false),
- inReadyRead(false)
+ winEventActPosted(false)
{
ZeroMemory(&overlapped, sizeof(OVERLAPPED));
overlapped.hEvent = eventHandle;
@@ -424,10 +422,8 @@ bool QWindowsPipeReader::consumePendingAndEmit(bool allowWinActPosting)
if (state != Running)
return false;
- if (emitReadyRead && !inReadyRead) {
- QScopedValueRollback<bool> guard(inReadyRead, true);
+ if (emitReadyRead)
emit readyRead();
- }
if (emitPipeClosed) {
if (dwError != ERROR_BROKEN_PIPE && dwError != ERROR_PIPE_NOT_CONNECTED)
emit winError(dwError, QLatin1String("QWindowsPipeReader::consumePendingAndEmit"));
@@ -484,8 +480,7 @@ bool QWindowsPipeReader::waitForNotification(const QDeadlineTimer &deadline)
/*!
Waits for the completion of the asynchronous read operation.
- Returns \c true, if we've emitted the readyRead signal (non-recursive case)
- or readyRead will be emitted by the event loop (recursive case).
+ Returns \c true, if we've emitted the readyRead signal.
*/
bool QWindowsPipeReader::waitForReadyRead(int msecs)
{
diff --git a/src/corelib/io/qwindowspipereader_p.h b/src/corelib/io/qwindowspipereader_p.h
index 8ae292da46..f02bf99880 100644
--- a/src/corelib/io/qwindowspipereader_p.h
+++ b/src/corelib/io/qwindowspipereader_p.h
@@ -125,7 +125,6 @@ private:
bool pipeBroken;
bool readyReadPending;
bool winEventActPosted;
- bool inReadyRead;
};
QT_END_NAMESPACE
diff --git a/src/corelib/io/qwindowspipewriter.cpp b/src/corelib/io/qwindowspipewriter.cpp
index 4b075549d0..f2f4dce056 100644
--- a/src/corelib/io/qwindowspipewriter.cpp
+++ b/src/corelib/io/qwindowspipewriter.cpp
@@ -39,7 +39,6 @@
****************************************************************************/
#include "qwindowspipewriter_p.h"
-#include <qscopedvaluerollback.h>
#include <qcoreapplication.h>
#include <QMutexLocker>
@@ -56,8 +55,7 @@ QWindowsPipeWriter::QWindowsPipeWriter(HANDLE pipeWriteEnd, QObject *parent)
stopped(true),
writeSequenceStarted(false),
bytesWrittenPending(false),
- winEventActPosted(false),
- inBytesWritten(false)
+ winEventActPosted(false)
{
ZeroMemory(&overlapped, sizeof(OVERLAPPED));
overlapped.hEvent = eventHandle;
@@ -290,12 +288,7 @@ bool QWindowsPipeWriter::consumePendingAndEmit(bool allowWinActPosting)
if (stopped)
return false;
- emit canWrite();
- if (!inBytesWritten) {
- QScopedValueRollback<bool> guard(inBytesWritten, true);
- emit bytesWritten(numberOfBytesWritten);
- }
-
+ emit bytesWritten(numberOfBytesWritten);
return true;
}
@@ -317,8 +310,7 @@ bool QWindowsPipeWriter::waitForNotification(const QDeadlineTimer &deadline)
/*!
Waits for the completion of the asynchronous write operation.
- Returns \c true, if we've emitted the bytesWritten signal (non-recursive case)
- or bytesWritten will be emitted by the event loop (recursive case).
+ Returns \c true, if we've emitted the bytesWritten signal.
*/
bool QWindowsPipeWriter::waitForWrite(int msecs)
{
diff --git a/src/corelib/io/qwindowspipewriter_p.h b/src/corelib/io/qwindowspipewriter_p.h
index d33c2753a8..cb78195a85 100644
--- a/src/corelib/io/qwindowspipewriter_p.h
+++ b/src/corelib/io/qwindowspipewriter_p.h
@@ -77,7 +77,6 @@ public:
HANDLE syncEvent() const { return syncHandle; }
Q_SIGNALS:
- void canWrite();
void bytesWritten(qint64 bytes);
protected:
@@ -104,7 +103,6 @@ private:
bool writeSequenceStarted;
bool bytesWrittenPending;
bool winEventActPosted;
- bool inBytesWritten;
};
QT_END_NAMESPACE
diff --git a/src/corelib/serialization/qxmlstream.cpp b/src/corelib/serialization/qxmlstream.cpp
index 769b33931e..5f9bed99b8 100644
--- a/src/corelib/serialization/qxmlstream.cpp
+++ b/src/corelib/serialization/qxmlstream.cpp
@@ -1297,6 +1297,11 @@ inline int QXmlStreamReaderPrivate::fastScanName(int *prefix)
int n = 0;
uint c;
while ((c = getChar()) != StreamEOF) {
+ if (n >= 4096) {
+ // This is too long to be a sensible name, and
+ // can exhaust memory
+ return 0;
+ }
switch (c) {
case '\n':
case ' ':
diff --git a/src/network/socket/qlocalsocket_p.h b/src/network/socket/qlocalsocket_p.h
index a80c09b517..34dc37870c 100644
--- a/src/network/socket/qlocalsocket_p.h
+++ b/src/network/socket/qlocalsocket_p.h
@@ -133,6 +133,8 @@ public:
#elif defined(Q_OS_WIN)
~QLocalSocketPrivate();
void destroyPipeHandles();
+ void _q_canRead();
+ void _q_bytesWritten(qint64 bytes);
void _q_canWrite();
void _q_pipeClosed();
void _q_winError(ulong windowsError, const QString &function);
@@ -161,6 +163,10 @@ public:
QLocalSocket::LocalSocketState state;
QString serverName;
QString fullServerName;
+#if defined(Q_OS_WIN) && !defined(QT_LOCALSOCKET_TCP)
+ bool emittedReadyRead;
+ bool emittedBytesWritten;
+#endif
Q_OBJECT_BINDABLE_PROPERTY(QLocalSocketPrivate, QLocalSocket::SocketOptions, socketOptions)
};
diff --git a/src/network/socket/qlocalsocket_win.cpp b/src/network/socket/qlocalsocket_win.cpp
index 677b431265..66eed86501 100644
--- a/src/network/socket/qlocalsocket_win.cpp
+++ b/src/network/socket/qlocalsocket_win.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include "qlocalsocket_p.h"
+#include <qscopedvaluerollback.h>
QT_BEGIN_NAMESPACE
@@ -45,7 +46,8 @@ void QLocalSocketPrivate::init()
{
Q_Q(QLocalSocket);
pipeReader = new QWindowsPipeReader(q);
- q->connect(pipeReader, SIGNAL(readyRead()), SIGNAL(readyRead()));
+ QObjectPrivate::connect(pipeReader, &QWindowsPipeReader::readyRead,
+ this, &QLocalSocketPrivate::_q_canRead);
q->connect(pipeReader, SIGNAL(pipeClosed()), SLOT(_q_pipeClosed()), Qt::QueuedConnection);
q->connect(pipeReader, SIGNAL(winError(ulong,QString)), SLOT(_q_winError(ulong,QString)));
}
@@ -99,7 +101,9 @@ QLocalSocketPrivate::QLocalSocketPrivate() : QIODevicePrivate(),
pipeWriter(0),
pipeReader(0),
error(QLocalSocket::UnknownSocketError),
- state(QLocalSocket::UnconnectedState)
+ state(QLocalSocket::UnconnectedState),
+ emittedReadyRead(false),
+ emittedBytesWritten(false)
{
writeBufferChunkSize = QIODEVICE_BUFFERSIZE;
}
@@ -223,10 +227,8 @@ qint64 QLocalSocket::writeData(const char *data, qint64 len)
d->write(data, len);
if (!d->pipeWriter) {
d->pipeWriter = new QWindowsPipeWriter(d->handle, this);
- connect(d->pipeWriter, &QWindowsPipeWriter::bytesWritten,
- this, &QLocalSocket::bytesWritten);
- QObjectPrivate::connect(d->pipeWriter, &QWindowsPipeWriter::canWrite,
- d, &QLocalSocketPrivate::_q_canWrite);
+ QObjectPrivate::connect(d->pipeWriter, &QWindowsPipeWriter::bytesWritten,
+ d, &QLocalSocketPrivate::_q_bytesWritten);
}
d->_q_canWrite();
return len;
@@ -242,6 +244,15 @@ void QLocalSocket::abort()
close();
}
+void QLocalSocketPrivate::_q_canRead()
+{
+ Q_Q(QLocalSocket);
+ if (!emittedReadyRead) {
+ QScopedValueRollback<bool> guard(emittedReadyRead, true);
+ emit q->readyRead();
+ }
+}
+
void QLocalSocketPrivate::_q_pipeClosed()
{
Q_Q(QLocalSocket);
@@ -359,6 +370,16 @@ bool QLocalSocket::setSocketDescriptor(qintptr socketDescriptor,
return true;
}
+void QLocalSocketPrivate::_q_bytesWritten(qint64 bytes)
+{
+ Q_Q(QLocalSocket);
+ if (!emittedBytesWritten) {
+ QScopedValueRollback<bool> guard(emittedBytesWritten, true);
+ emit q->bytesWritten(bytes);
+ }
+ _q_canWrite();
+}
+
void QLocalSocketPrivate::_q_canWrite()
{
Q_Q(QLocalSocket);
diff --git a/src/testlib/CMakeLists.txt b/src/testlib/CMakeLists.txt
index da53bb8b68..32e44c2d07 100644
--- a/src/testlib/CMakeLists.txt
+++ b/src/testlib/CMakeLists.txt
@@ -25,6 +25,7 @@ qt_internal_add_module(Test
qemulationdetector_p.h
qjunittestlogger.cpp qjunittestlogger_p.h
qplaintestlogger.cpp qplaintestlogger_p.h
+ qpropertytesthelper_p.h
qsignaldumper.cpp qsignaldumper_p.h
qsignalspy.h
qtaptestlogger.cpp qtaptestlogger_p.h
diff --git a/src/testlib/qpropertytesthelper_p.h b/src/testlib/qpropertytesthelper_p.h
new file mode 100644
index 0000000000..c89ff58051
--- /dev/null
+++ b/src/testlib/qpropertytesthelper_p.h
@@ -0,0 +1,304 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtTest module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPROPERTYTESTHELPER_P_H
+#define QPROPERTYTESTHELPER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/QObject>
+#include <QtTest/QSignalSpy>
+#include <QTest>
+
+QT_BEGIN_NAMESPACE
+
+namespace QTestPrivate {
+
+/*!
+ \internal
+
+ This helper macro is used as a wrapper around \l QVERIFY2() to provide a
+ detailed error message in case of failure. It is intended to be used \e only
+ in the helper functions below.
+
+ The custom \a comparator method is used to check if the \a actual and
+ \a expected values are equal or not.
+
+ The macro uses a custom \a represent callback to generate the string
+ representation of \a actual and \a expected.
+
+ The error message is close to the one provided by the \l QCOMPARE() macro.
+ Specifically the implementation is taken from the \c formatFailMessage()
+ function, which is defined in the \c qtestresult.cpp file.
+*/
+#define QPROPERTY_TEST_COMPARISON_HELPER(actual, expected, comparator, represent) \
+ do { \
+ const size_t maxMsgLen = 1024; \
+ char msg[maxMsgLen] = { '\0' }; \
+ auto actualStr = represent(actual); \
+ auto expectedStr = represent(expected); \
+ const size_t len1 = mbstowcs(nullptr, #actual, maxMsgLen); \
+ const size_t len2 = mbstowcs(nullptr, #expected, maxMsgLen); \
+ qsnprintf(msg, maxMsgLen, "\n%s\n Actual (%s)%*s %s\n Expected (%s)%*s %s\n", \
+ "Comparison failed!", #actual, qMax(len1, len2) - len1 + 1, ":", \
+ actualStr ? actualStr : "<null>", #expected, qMax(len1, len2) - len2 + 1, ":", \
+ expectedStr ? expectedStr : "<null>"); \
+ delete[] actualStr; \
+ delete[] expectedStr; \
+ QVERIFY2(comparator(actual, expected), msg); \
+ } while (false)
+
+/*!
+ \internal
+ Basic testing of a bindable property.
+
+ This helper function tests the behavior of bindable read/write property
+ \a propertyName, of type \c PropertyType, in class \c TestedClass.
+ The caller must supply an \a instance of \c TestedClass and two distinct
+ values, \a initial and \a changed, of \c PropertyType.
+
+ Since the first part of the test sets the property to \a initial, it
+ \e {must not} be the default value of the property, or the check that it
+ was set will be vacuous.
+
+ By default \c {operator==()} is used to compare values of the property and
+ \c {QTest::toString()} is used to generate proper error messages.
+
+ If such comparison is not supported for \c PropertyType, or the comparison
+ it supports is not appropriate to this property, a custom \a comparator can
+ be supplied.
+
+ Apart from that, a custom \a represent callback can also be specified to
+ generate a string representation of \c PropertyType. If supplied, it must
+ allocate its returned string using \c {new char[]}, so that it can be used
+ in place of \l {QTest::toString()}.
+
+ \note Any test calling this method will need to call
+ \code
+ if (QTest::currentTestFailed())
+ return;
+ \endcode
+ after doing so, if there is any later code in the test. If testing several
+ properties in one test method, emitting a debug message saying which
+ property failed, before returning, is a kindness to readers of the output.
+*/
+template<typename TestedClass, typename PropertyType>
+void testReadWritePropertyBasics(
+ TestedClass &instance, const PropertyType &initial, const PropertyType &changed,
+ const char *propertyName,
+ std::function<bool(const PropertyType &, const PropertyType &)> comparator =
+ [](const PropertyType &lhs, const PropertyType &rhs) { return lhs == rhs; },
+ std::function<char *(const PropertyType &)> represent =
+ [](const PropertyType &val) { return QTest::toString(val); })
+{
+ // get the property
+ const QMetaObject *metaObject = instance.metaObject();
+ QMetaProperty metaProperty = metaObject->property(metaObject->indexOfProperty(propertyName));
+
+ // in case the TestedClass has setProperty()/property() methods.
+ QObject &testedObj = static_cast<QObject &>(instance);
+
+ QVERIFY2(metaProperty.isBindable() && metaProperty.isWritable(),
+ "Preconditions not met for " + QByteArray(propertyName));
+
+ QScopedPointer<QSignalSpy> spy(nullptr);
+ if (metaProperty.hasNotifySignal())
+ spy.reset(new QSignalSpy(&instance, metaProperty.notifySignal()));
+
+ testedObj.setProperty(propertyName, QVariant::fromValue(initial));
+ QPROPERTY_TEST_COMPARISON_HELPER(
+ testedObj.property(propertyName).template value<PropertyType>(), initial, comparator,
+ represent);
+ if (spy)
+ QCOMPARE(spy->count(), 1);
+
+ QUntypedBindable bindable = metaProperty.bindable(&instance);
+
+ // Bind to the object's property (using both lambda and
+ // Qt:makePropertyBinding).
+ QProperty<PropertyType> propObserver(changed);
+ propObserver.setBinding(bindable.makeBinding());
+ QPROPERTY_TEST_COMPARISON_HELPER(propObserver.value(), initial, comparator, represent);
+
+ QProperty<PropertyType> propObserverLambda(changed);
+ propObserverLambda.setBinding(
+ [&]() { return testedObj.property(propertyName).template value<PropertyType>(); });
+ QPROPERTY_TEST_COMPARISON_HELPER(propObserverLambda.value(), initial, comparator, represent);
+
+ testedObj.setProperty(propertyName, QVariant::fromValue(changed));
+ QPROPERTY_TEST_COMPARISON_HELPER(propObserver.value(), changed, comparator, represent);
+ QPROPERTY_TEST_COMPARISON_HELPER(propObserverLambda.value(), changed, comparator, represent);
+ if (spy)
+ QCOMPARE(spy->count(), 2);
+
+ // Bind object's property to other property
+ QProperty<PropertyType> propSetter(initial);
+ QVERIFY(!bindable.hasBinding());
+ bindable.setBinding(Qt::makePropertyBinding(propSetter));
+
+ QVERIFY(bindable.hasBinding());
+ QPROPERTY_TEST_COMPARISON_HELPER(
+ testedObj.property(propertyName).template value<PropertyType>(), initial, comparator,
+ represent);
+ QPROPERTY_TEST_COMPARISON_HELPER(propObserver.value(), initial, comparator, represent);
+ QPROPERTY_TEST_COMPARISON_HELPER(propObserverLambda.value(), initial, comparator, represent);
+ if (spy)
+ QCOMPARE(spy->count(), 3);
+
+ // Count notifications triggered; should only happen on actual change.
+ int updateCount = 0;
+ auto handler = bindable.onValueChanged([&updateCount]() { ++updateCount; });
+ Q_UNUSED(handler)
+
+ propSetter.setValue(changed);
+ QPROPERTY_TEST_COMPARISON_HELPER(
+ testedObj.property(propertyName).template value<PropertyType>(), changed, comparator,
+ represent);
+ QPROPERTY_TEST_COMPARISON_HELPER(propObserver.value(), changed, comparator, represent);
+ QPROPERTY_TEST_COMPARISON_HELPER(propObserverLambda.value(), changed, comparator, represent);
+ QCOMPARE(updateCount, 1);
+ if (spy)
+ QCOMPARE(spy->count(), 4);
+
+ // Test that manually setting the value (even the same one) breaks the
+ // binding.
+ testedObj.setProperty(propertyName, QVariant::fromValue(changed));
+ QVERIFY(!bindable.hasBinding());
+ // Setting the same value should have no impact on udpateCount.
+ QCOMPARE(updateCount, 1);
+
+ // value didn't change -> the signal should not be emitted
+ if (spy)
+ QCOMPARE(spy->count(), 4);
+}
+
+/*!
+ \internal
+ Basic testing of a read-only bindable property.
+
+ This helper function tests the behavior of bindable read-only property
+ \a propertyName, of type \c PropertyType, in class \c TestedClass.
+ The caller must supply an \a instance of \c TestedClass and two distinct
+ values, \a initial and \a changed, of \c PropertyType.
+
+ When this function is called, the property's value must be \a initial.
+ The \a mutator must, when called, cause the property's value to be revised
+ to \a changed.
+
+ By default \c {operator==()} is used to compare values of the property and
+ \c {QTest::toString()} is used to generate proper error messages.
+
+ If such comparison is not supported for \c PropertyType, or the comparison
+ it supports is not appropriate to this property, a custom \a comparator can
+ be supplied.
+
+ Apart from that, a custom \a represent callback can also be specified to
+ generate a string representation of \c PropertyType. If supplied, it must
+ allocate its returned string using \c {new char[]}, so that it can be used
+ in place of \l {QTest::toString()}.
+
+ \note Any test calling this method will need to call
+ \code
+ if (QTest::currentTestFailed())
+ return;
+ \endcode
+ after doing so, if there is any later code in the test. If testing several
+ properties in one test method, emitting a debug message saying which
+ property failed, before returning, is a kindness to readers of the output.
+*/
+template<typename TestedClass, typename PropertyType>
+void testReadOnlyPropertyBasics(
+ TestedClass &instance, const PropertyType &initial, const PropertyType &changed,
+ const char *propertyName,
+ std::function<void()> mutator = []() { QFAIL("Data modifier function must be provided"); },
+ std::function<bool(const PropertyType &, const PropertyType &)> comparator =
+ [](const PropertyType &lhs, const PropertyType &rhs) { return lhs == rhs; },
+ std::function<char *(const PropertyType &)> represent =
+ [](const PropertyType &val) { return QTest::toString(val); })
+{
+ // get the property
+ const QMetaObject *metaObject = instance.metaObject();
+ QMetaProperty metaProperty = metaObject->property(metaObject->indexOfProperty(propertyName));
+
+ // in case the TestedClass has setProperty()/property() methods.
+ QObject &testedObj = static_cast<QObject &>(instance);
+
+ QVERIFY2(metaProperty.isBindable(), "Preconditions not met for " + QByteArray(propertyName));
+
+ QUntypedBindable bindable = metaProperty.bindable(&instance);
+
+ QScopedPointer<QSignalSpy> spy(nullptr);
+ if (metaProperty.hasNotifySignal())
+ spy.reset(new QSignalSpy(&instance, metaProperty.notifySignal()));
+
+ QPROPERTY_TEST_COMPARISON_HELPER(
+ testedObj.property(propertyName).template value<PropertyType>(), initial, comparator,
+ represent);
+
+ QProperty<PropertyType> propObserver;
+ propObserver.setBinding(bindable.makeBinding());
+
+ QPROPERTY_TEST_COMPARISON_HELPER(propObserver.value(), initial, comparator, represent);
+
+ // Invoke mutator function. Now property value should be changed.
+ mutator();
+
+ QPROPERTY_TEST_COMPARISON_HELPER(
+ testedObj.property(propertyName).template value<PropertyType>(), changed, comparator,
+ represent);
+ QPROPERTY_TEST_COMPARISON_HELPER(propObserver.value(), changed, comparator, represent);
+
+ if (spy)
+ QCOMPARE(spy->count(), 1);
+}
+
+} // namespace QTestPrivate
+
+QT_END_NAMESPACE
+
+#endif // QPROPERTYTESTHELPER_P_H
diff --git a/tests/auto/corelib/io/qprocess/tst_qprocess.cpp b/tests/auto/corelib/io/qprocess/tst_qprocess.cpp
index 857c24c571..035c5941c2 100644
--- a/tests/auto/corelib/io/qprocess/tst_qprocess.cpp
+++ b/tests/auto/corelib/io/qprocess/tst_qprocess.cpp
@@ -1294,7 +1294,7 @@ void tst_QProcess::waitForReadyReadInAReadyReadSlot()
QVERIFY(process.waitForFinished(5000));
QCOMPARE(process.exitStatus(), QProcess::NormalExit);
QCOMPARE(process.exitCode(), 0);
- QVERIFY(process.bytesAvailable() > bytesAvailable);
+ QVERIFY(process.bytesAvailable() >= bytesAvailable);
}
void tst_QProcess::waitForReadyReadInAReadyReadSlotSlot()
@@ -1304,6 +1304,8 @@ void tst_QProcess::waitForReadyReadInAReadyReadSlotSlot()
bytesAvailable = process->bytesAvailable();
process->write("bar", 4);
QVERIFY(process->waitForReadyRead(5000));
+ QVERIFY(process->bytesAvailable() > bytesAvailable);
+ bytesAvailable = process->bytesAvailable();
QTestEventLoop::instance().exitLoop();
}