diff options
-rw-r--r-- | src/corelib/io/qprocess_p.h | 3 | ||||
-rw-r--r-- | src/corelib/io/qprocess_win.cpp | 18 | ||||
-rw-r--r-- | src/corelib/io/qwindowspipereader.cpp | 11 | ||||
-rw-r--r-- | src/corelib/io/qwindowspipereader_p.h | 1 | ||||
-rw-r--r-- | src/corelib/io/qwindowspipewriter.cpp | 14 | ||||
-rw-r--r-- | src/corelib/io/qwindowspipewriter_p.h | 2 | ||||
-rw-r--r-- | src/corelib/serialization/qxmlstream.cpp | 5 | ||||
-rw-r--r-- | src/network/socket/qlocalsocket_p.h | 6 | ||||
-rw-r--r-- | src/network/socket/qlocalsocket_win.cpp | 33 | ||||
-rw-r--r-- | src/testlib/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/testlib/qpropertytesthelper_p.h | 304 | ||||
-rw-r--r-- | tests/auto/corelib/io/qprocess/tst_qprocess.cpp | 4 |
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(); } |