Fix integer overflows in QDeadlineTimer

If the deadline is far in the future, the conversions to nanoseconds
or internal arithmetic may overflow and give an invalid object, thus
the deadline may end up in the past. Added a test to the testlib
selftest for sleep.

[ChangeLog][QtCore][QDeadlineTimer] Fixed integer overflows
leading to immediate timeouts.

Task-number: QTBUG-69750
Change-Id: I9814eccdf9f9b3add9ca66ec3e27e10cd5ad54a8
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Konstantin Shegunov 2018-08-01 00:50:14 +03:00
parent 8adcfa8b24
commit c6bee8e4b2
10 changed files with 501 additions and 67 deletions

View File

@ -39,18 +39,294 @@
#include "qdeadlinetimer.h"
#include "qdeadlinetimer_p.h"
#include "private/qnumeric_p.h"
QT_BEGIN_NAMESPACE
Q_DECL_CONST_FUNCTION static inline QPair<qint64, qint64> toSecsAndNSecs(qint64 nsecs)
{
qint64 secs = nsecs / (1000*1000*1000);
if (nsecs < 0)
--secs;
nsecs -= secs * 1000*1000*1000;
return qMakePair(secs, nsecs);
namespace {
class TimeReference
{
enum : unsigned {
umega = 1000 * 1000,
ugiga = umega * 1000
};
enum : qint64 {
kilo = 1000,
mega = kilo * 1000,
giga = mega * 1000
};
public:
enum RoundingStrategy {
RoundDown,
RoundUp,
RoundDefault = RoundDown
};
static constexpr qint64 Min = std::numeric_limits<qint64>::min();
static constexpr qint64 Max = std::numeric_limits<qint64>::max();
inline TimeReference(qint64 = 0, unsigned = 0);
inline void updateTimer(qint64 &, unsigned &);
inline bool addNanoseconds(qint64);
inline bool addMilliseconds(qint64);
bool addSecsAndNSecs(qint64, qint64);
inline bool subtract(const qint64, const unsigned);
inline bool toMilliseconds(qint64 *, RoundingStrategy = RoundDefault) const;
inline bool toNanoseconds(qint64 *) const;
inline void saturate(bool toMax);
static bool sign(qint64, qint64);
private:
bool adjust(const qint64, const unsigned, qint64 = 0);
private:
qint64 secs;
unsigned nsecs;
};
}
inline TimeReference::TimeReference(qint64 t1, unsigned t2)
: secs(t1), nsecs(t2)
{
}
inline void TimeReference::updateTimer(qint64 &t1, unsigned &t2)
{
t1 = secs;
t2 = nsecs;
}
inline void TimeReference::saturate(bool toMax)
{
secs = toMax ? Max : Min;
}
/*!
* \internal
*
* Determines the sign of a (seconds, nanoseconds) pair
* for differentiating overflow from underflow. It doesn't
* deal with equality as it shouldn't ever be called in that case.
*
* Returns true if the pair represents a positive time offset
* false otherwise.
*/
bool TimeReference::sign(qint64 secs, qint64 nsecs)
{
if (secs > 0) {
if (nsecs > 0)
return true;
} else {
if (nsecs < 0)
return false;
}
// They are different in sign
secs += nsecs / giga;
if (secs > 0)
return true;
else if (secs < 0)
return false;
// We should never get over|underflow out of
// the case: secs * giga == -nsecs
// So the sign of nsecs is the deciding factor
Q_ASSERT(nsecs % giga != 0);
return nsecs > 0;
}
#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)
inline bool TimeReference::addNanoseconds(qint64 arg)
{
return addSecsAndNSecs(arg / giga, arg % giga);
}
inline bool TimeReference::addMilliseconds(qint64 arg)
{
return addSecsAndNSecs(arg / kilo, (arg % kilo) * mega);
}
/*!
* \internal
*
* Adds \a t1 addSecs seconds and \a addNSecs nanoseconds to the
* time reference. The arguments are normalized to seconds (qint64)
* and nanoseconds (unsigned) before the actual calculation is
* delegated to adjust(). If the nanoseconds are negative the
* owed second used for the normalization is passed on to adjust()
* as third argument.
*
* Returns true if operation was successful, false on over|underflow
*/
bool TimeReference::addSecsAndNSecs(qint64 addSecs, qint64 addNSecs)
{
// Normalize the arguments
if (qAbs(addNSecs) >= giga) {
if (add_overflow<qint64>(addSecs, addNSecs / giga, &addSecs))
return false;
addNSecs %= giga;
}
if (addNSecs < 0)
return adjust(addSecs, ugiga - unsigned(-addNSecs), -1);
return adjust(addSecs, unsigned(addNSecs));
}
/*!
* \internal
*
* Adds \a t1 seconds and \a t2 nanoseconds to the internal members.
* Takes into account the additional \a carrySeconds we may owe or need to carry over.
*
* Returns true if operation was successful, false on over|underflow
*/
bool TimeReference::adjust(const qint64 t1, const unsigned t2, qint64 carrySeconds)
{
Q_STATIC_ASSERT(QDeadlineTimerNanosecondsInT2);
nsecs += t2;
if (nsecs >= ugiga) {
nsecs -= ugiga;
carrySeconds++;
}
// We don't worry about the order of addition, because the result returned by
// callers of this function is unchanged regardless of us over|underflowing.
// If we do, we do so by no more than a second, thus saturating the timer to
// Forever has the same effect as if we did the arithmetic exactly and salvaged
// the overflow.
return !add_overflow<qint64>(secs, t1, &secs) && !add_overflow<qint64>(secs, carrySeconds, &secs);
}
/*!
* \internal
*
* Subtracts \a t1 seconds and \a t2 nanoseconds from the time reference.
* When normalizing the nanoseconds to a positive number the owed seconds is
* passed as third argument to adjust() as the seconds may over|underflow
* if we do the calculation directly. There is little sense to check the
* seconds for over|underflow here in case we are going to need to carry
* over a second _after_ we add the nanoseconds.
*
* Returns true if operation was successful, false on over|underflow
*/
inline bool TimeReference::subtract(const qint64 t1, const unsigned t2)
{
Q_ASSERT(t2 < ugiga);
return adjust(-t1, ugiga - t2, -1);
}
/*!
* \internal
*
* Converts the time reference to milliseconds.
*
* Checks are done without making use of mul_overflow because it may
* not be implemented on some 32bit platforms.
*
* Returns true if operation was successful, false on over|underflow
*/
inline bool TimeReference::toMilliseconds(qint64 *result, RoundingStrategy rounding) const
{
static constexpr qint64 maxSeconds = Max / kilo;
static constexpr qint64 minSeconds = Min / kilo;
if (secs > maxSeconds || secs < minSeconds)
return false;
unsigned ns = rounding == RoundDown ? nsecs : nsecs + umega - 1;
return !add_overflow<qint64>(secs * kilo, ns / umega, result);
}
/*!
* \internal
*
* Converts the time reference to nanoseconds.
*
* Checks are done without making use of mul_overflow because it may
* not be implemented on some 32bit platforms.
*
* Returns true if operation was successful, false on over|underflow
*/
inline bool TimeReference::toNanoseconds(qint64 *result) const
{
static constexpr qint64 maxSeconds = Max / giga;
static constexpr qint64 minSeconds = Min / giga;
if (secs > maxSeconds || secs < minSeconds)
return false;
return !add_overflow<qint64>(secs * giga, nsecs, result);
}
#else
inline bool TimeReference::addNanoseconds(qint64 arg)
{
return adjust(arg, 0);
}
inline bool TimeReference::addMilliseconds(qint64 arg)
{
static constexpr qint64 maxMilliseconds = Max / mega;
if (qAbs(arg) > maxMilliseconds)
return false;
return addNanoseconds(arg * mega);
}
inline bool TimeReference::addSecsAndNSecs(qint64 addSecs, qint64 addNSecs)
{
static constexpr qint64 maxSeconds = Max / giga;
static constexpr qint64 minSeconds = Min / giga;
if (addSecs > maxSeconds || addSecs < minSeconds || add_overflow<qint64>(addSecs * giga, addNSecs, &addNSecs))
return false;
return addNanoseconds(addNSecs);
}
inline bool TimeReference::adjust(const qint64 t1, const unsigned t2, qint64 carrySeconds)
{
Q_STATIC_ASSERT(!QDeadlineTimerNanosecondsInT2);
Q_UNUSED(t2);
Q_UNUSED(carrySeconds);
return !add_overflow<qint64>(secs, t1, &secs);
}
inline bool TimeReference::subtract(const qint64 t1, const unsigned t2)
{
Q_UNUSED(t2);
return addNanoseconds(-t1);
}
inline bool TimeReference::toMilliseconds(qint64 *result, RoundingStrategy rounding) const
{
// Force QDeadlineTimer to treat the border cases as
// over|underflow and saturate the results returned to the user.
// We don't want to get valid milliseconds out of saturated timers.
if (secs == Max || secs == Min)
return false;
*result = secs / mega;
if (rounding == RoundUp && secs > *result * mega)
(*result)++;
return true;
}
inline bool TimeReference::toNanoseconds(qint64 *result) const
{
*result = secs;
return true;
}
#endif
/*!
\class QDeadlineTimer
\inmodule QtCore
@ -262,10 +538,17 @@ QDeadlineTimer::QDeadlineTimer(qint64 msecs, Qt::TimerType type) Q_DECL_NOTHROW
*/
void QDeadlineTimer::setRemainingTime(qint64 msecs, Qt::TimerType timerType) Q_DECL_NOTHROW
{
if (msecs == -1)
if (msecs == -1) {
*this = QDeadlineTimer(Forever, timerType);
else
setPreciseRemainingTime(0, msecs * 1000 * 1000, timerType);
return;
}
*this = current(timerType);
TimeReference ref(t1, t2);
if (!ref.addMilliseconds(msecs))
ref.saturate(msecs > 0);
ref.updateTimer(t1, t2);
}
/*!
@ -287,16 +570,10 @@ void QDeadlineTimer::setPreciseRemainingTime(qint64 secs, qint64 nsecs, Qt::Time
}
*this = current(timerType);
if (QDeadlineTimerNanosecondsInT2) {
t1 += secs + toSecsAndNSecs(nsecs).first;
t2 += toSecsAndNSecs(nsecs).second;
if (t2 > 1000*1000*1000) {
t2 -= 1000*1000*1000;
++t1;
}
} else {
t1 += secs * 1000 * 1000 * 1000 + nsecs;
}
TimeReference ref(t1, t2);
if (!ref.addSecsAndNSecs(secs, nsecs))
ref.saturate(TimeReference::sign(secs, nsecs));
ref.updateTimer(t1, t2);
}
/*!
@ -391,8 +668,22 @@ void QDeadlineTimer::setTimerType(Qt::TimerType timerType)
*/
qint64 QDeadlineTimer::remainingTime() const Q_DECL_NOTHROW
{
qint64 ns = remainingTimeNSecs();
return ns <= 0 ? ns : (ns + 999999) / (1000 * 1000);
if (isForever())
return -1;
QDeadlineTimer now = current(timerType());
TimeReference ref(t1, t2);
qint64 msecs;
if (!ref.subtract(now.t1, now.t2))
return 0; // We can only underflow here
// If we fail the conversion, t1 < now.t1 means we underflowed,
// thus the deadline had long expired
if (!ref.toMilliseconds(&msecs, TimeReference::RoundUp))
return t1 < now.t1 ? 0 : -1;
return msecs < 0 ? 0 : msecs;
}
/*!
@ -414,14 +705,23 @@ qint64 QDeadlineTimer::remainingTimeNSecs() const Q_DECL_NOTHROW
/*!
\internal
Same as remainingTimeNSecs, but may return negative remaining times. Does
not deal with Forever.
not deal with Forever. In case of underflow the result is saturated to
the minimum possible value, on overflow - the maximum possible value.
*/
qint64 QDeadlineTimer::rawRemainingTimeNSecs() const Q_DECL_NOTHROW
{
QDeadlineTimer now = current(timerType());
if (QDeadlineTimerNanosecondsInT2)
return (t1 - now.t1) * (1000*1000*1000) + t2 - now.t2;
return t1 - now.t1;
TimeReference ref(t1, t2);
qint64 nsecs;
if (!ref.subtract(now.t1, now.t2))
return TimeReference::Min; // We can only underflow here
// If we fail the conversion, t1 < now.t1 means we underflowed,
// thus the deadline had long expired
if (!ref.toNanoseconds(&nsecs))
return t1 < now.t1 ? TimeReference::Min : TimeReference::Max;
return nsecs;
}
/*!
@ -447,8 +747,13 @@ qint64 QDeadlineTimer::rawRemainingTimeNSecs() const Q_DECL_NOTHROW
qint64 QDeadlineTimer::deadline() const Q_DECL_NOTHROW
{
if (isForever())
return t1;
return deadlineNSecs() / (1000 * 1000);
return TimeReference::Max;
qint64 result;
if (!TimeReference(t1, t2).toMilliseconds(&result))
return t1 < 0 ? TimeReference::Min : TimeReference::Max;
return result;
}
/*!
@ -457,7 +762,8 @@ qint64 QDeadlineTimer::deadline() const Q_DECL_NOTHROW
same as QElapsedTimer::msecsSinceReference(). The value will be in the past
if this QDeadlineTimer has expired.
If this QDeadlineTimer never expires, this function returns
If this QDeadlineTimer never expires or the number of nanoseconds until the
deadline can't be accommodated in the return type, this function returns
\c{std::numeric_limits<qint64>::max()}.
This function can be used to calculate the amount of time a timer is
@ -474,10 +780,13 @@ qint64 QDeadlineTimer::deadline() const Q_DECL_NOTHROW
qint64 QDeadlineTimer::deadlineNSecs() const Q_DECL_NOTHROW
{
if (isForever())
return t1;
if (QDeadlineTimerNanosecondsInT2)
return t1 * 1000 * 1000 * 1000 + t2;
return t1;
return TimeReference::Max;
qint64 result;
if (!TimeReference(t1, t2).toNanoseconds(&result))
return t1 < 0 ? TimeReference::Min : TimeReference::Max;
return result;
}
/*!
@ -487,18 +796,25 @@ qint64 QDeadlineTimer::deadlineNSecs() const Q_DECL_NOTHROW
timerType. If the value is in the past, this QDeadlineTimer will be marked
as expired.
If \a msecs is \c{std::numeric_limits<qint64>::max()}, this QDeadlineTimer
will be set to never expire.
If \a msecs is \c{std::numeric_limits<qint64>::max()} or the deadline is
beyond a representable point in the future, this QDeadlineTimer will be set
to never expire.
\sa setPreciseDeadline(), deadline(), deadlineNSecs(), setRemainingTime()
*/
void QDeadlineTimer::setDeadline(qint64 msecs, Qt::TimerType timerType) Q_DECL_NOTHROW
{
if (msecs == (std::numeric_limits<qint64>::max)()) {
setPreciseDeadline(msecs, 0, timerType); // msecs == MAX implies Forever
} else {
setPreciseDeadline(msecs / 1000, msecs % 1000 * 1000 * 1000, timerType);
if (msecs == TimeReference::Max) {
*this = QDeadlineTimer(Forever, timerType);
return;
}
type = timerType;
TimeReference ref;
if (!ref.addMilliseconds(msecs))
ref.saturate(msecs > 0);
ref.updateTimer(t1, t2);
}
/*!
@ -516,14 +832,13 @@ void QDeadlineTimer::setDeadline(qint64 msecs, Qt::TimerType timerType) Q_DECL_N
void QDeadlineTimer::setPreciseDeadline(qint64 secs, qint64 nsecs, Qt::TimerType timerType) Q_DECL_NOTHROW
{
type = timerType;
if (secs == (std::numeric_limits<qint64>::max)() || nsecs == (std::numeric_limits<qint64>::max)()) {
*this = QDeadlineTimer(Forever, timerType);
} else if (QDeadlineTimerNanosecondsInT2) {
t1 = secs + toSecsAndNSecs(nsecs).first;
t2 = toSecsAndNSecs(nsecs).second;
} else {
t1 = secs * (1000*1000*1000) + nsecs;
}
// We don't pass the seconds to the constructor, because we don't know
// at this point if t1 holds the seconds or nanoseconds; it's platform specific.
TimeReference ref;
if (!ref.addSecsAndNSecs(secs, nsecs))
ref.saturate(TimeReference::sign(secs, nsecs));
ref.updateTimer(t1, t2);
}
/*!
@ -536,18 +851,14 @@ void QDeadlineTimer::setPreciseDeadline(qint64 secs, qint64 nsecs, Qt::TimerType
*/
QDeadlineTimer QDeadlineTimer::addNSecs(QDeadlineTimer dt, qint64 nsecs) Q_DECL_NOTHROW
{
if (dt.isForever() || nsecs == (std::numeric_limits<qint64>::max)()) {
dt = QDeadlineTimer(Forever, dt.timerType());
} else if (QDeadlineTimerNanosecondsInT2) {
dt.t1 += toSecsAndNSecs(nsecs).first;
dt.t2 += toSecsAndNSecs(nsecs).second;
if (dt.t2 > 1000*1000*1000) {
dt.t2 -= 1000*1000*1000;
++dt.t1;
}
} else {
dt.t1 += nsecs;
}
if (dt.isForever())
return dt;
TimeReference ref(dt.t1, dt.t2);
if (!ref.addNanoseconds(nsecs))
ref.saturate(nsecs > 0);
ref.updateTimer(dt.t1, dt.t2);
return dt;
}
@ -656,6 +967,19 @@ QDeadlineTimer QDeadlineTimer::addNSecs(QDeadlineTimer dt, qint64 nsecs) Q_DECL_
To add times of precision greater than 1 millisecond, use addNSecs().
*/
QDeadlineTimer operator+(QDeadlineTimer dt, qint64 msecs)
{
if (dt.isForever())
return dt;
TimeReference ref(dt.t1, dt.t2);
if (!ref.addMilliseconds(msecs))
ref.saturate(msecs > 0);
ref.updateTimer(dt.t1, dt.t2);
return dt;
}
/*!
\fn QDeadlineTimer operator+(qint64 msecs, QDeadlineTimer dt)
\relates QDeadlineTimer

View File

@ -108,8 +108,7 @@ public:
friend bool operator>=(QDeadlineTimer d1, QDeadlineTimer d2) Q_DECL_NOTHROW
{ return !(d1 < d2); }
friend QDeadlineTimer operator+(QDeadlineTimer dt, qint64 msecs)
{ return QDeadlineTimer::addNSecs(dt, msecs * 1000 * 1000); }
friend Q_CORE_EXPORT QDeadlineTimer operator+(QDeadlineTimer dt, qint64 msecs);
friend QDeadlineTimer operator+(qint64 msecs, QDeadlineTimer dt)
{ return dt + msecs; }
friend QDeadlineTimer operator-(QDeadlineTimer dt, qint64 msecs)

View File

@ -29,6 +29,7 @@
#include <QtCore/QString>
#include <QtCore/QTime>
#include <QtCore/QDeadlineTimer>
#include <QtCore/QElapsedTimer>
#include <QtTest/QtTest>
#if QT_HAS_INCLUDE(<chrono>)
@ -50,6 +51,7 @@ private Q_SLOTS:
void current();
void deadlines();
void setDeadline();
void overflow();
void expire();
void stdchrono();
};
@ -417,6 +419,83 @@ void tst_QDeadlineTimer::setDeadline()
QCOMPARE(deadline.deadlineNSecs(), nsec);
}
void tst_QDeadlineTimer::overflow()
{
QFETCH_GLOBAL(Qt::TimerType, timerType);
// Check the constructor for overflows (should also cover saturating the result of the deadline() method if overflowing)
QDeadlineTimer now = QDeadlineTimer::current(timerType), deadline(std::numeric_limits<qint64>::max() - 1, timerType);
QVERIFY(deadline.isForever() || deadline.deadline() >= now.deadline());
// Check the setDeadline with milliseconds (should also cover implicitly setting the nanoseconds as qint64 max)
deadline.setDeadline(std::numeric_limits<qint64>::max() - 1, timerType);
QVERIFY(deadline.isForever() || deadline.deadline() >= now.deadline());
// Check the setRemainingTime with milliseconds (should also cover implicitly setting the nanoseconds as qint64 max)
deadline.setRemainingTime(std::numeric_limits<qint64>::max() - 1, timerType);
QVERIFY(deadline.isForever() || deadline.deadline() >= now.deadline());
// Check that the deadline gets saturated when the arguments of setPreciseDeadline are large
deadline.setPreciseDeadline(std::numeric_limits<qint64>::max() - 1, std::numeric_limits<qint64>::max() - 1, timerType);
QCOMPARE(deadline.deadline(), std::numeric_limits<qint64>::max());
QVERIFY(deadline.isForever());
// Check that remainingTime gets saturated if we overflow
deadline.setPreciseRemainingTime(std::numeric_limits<qint64>::max() - 1, std::numeric_limits<qint64>::max() - 1, timerType);
QCOMPARE(deadline.remainingTime(), qint64(-1));
QVERIFY(deadline.isForever());
// Check that we saturate the getter for nanoseconds
deadline.setPreciseDeadline(std::numeric_limits<qint64>::max() - 1, 0, timerType);
QCOMPARE(deadline.deadlineNSecs(), std::numeric_limits<qint64>::max());
// Check that adding nanoseconds and overflowing is consistent and saturates the timer
deadline = QDeadlineTimer::addNSecs(deadline, std::numeric_limits<qint64>::max() - 1);
QVERIFY(deadline.isForever());
// Make sure forever is forever, regardless of us subtracting time from it
deadline = QDeadlineTimer(QDeadlineTimer::Forever, timerType);
deadline = QDeadlineTimer::addNSecs(deadline, -10000);
QVERIFY(deadline.isForever());
// Make sure we get the correct result when moving the deadline back and forth in time
QDeadlineTimer current = QDeadlineTimer::current(timerType);
QDeadlineTimer takenNSecs = QDeadlineTimer::addNSecs(current, -1000);
QVERIFY(takenNSecs.deadlineNSecs() - current.deadlineNSecs() == -1000);
QDeadlineTimer addedNSecs = QDeadlineTimer::addNSecs(current, 1000);
QVERIFY(addedNSecs.deadlineNSecs() - current.deadlineNSecs() == 1000);
// Make sure the calculation goes as expected when we need to subtract nanoseconds
// We make use of an additional timer to be certain that
// even when the environment is under load we can track the
// time needed to do the calls
static constexpr qint64 nsExpected = 1000 * 1000 * 1000 - 1000; // 1s - 1000ns, what we pass to setPreciseRemainingTime() later
QElapsedTimer callTimer;
callTimer.start();
deadline = QDeadlineTimer::current(timerType);
qint64 nsDeadline = deadline.deadlineNSecs();
// We adjust in relation to current() here, so we expect the difference to be a tad over the exact number.
// However we are tracking the elapsed time, so it shouldn't be a problem.
deadline.setPreciseRemainingTime(1, -1000, timerType);
qint64 difference = (deadline.deadlineNSecs() - nsDeadline) - nsExpected;
QVERIFY(difference >= 0); // Should always be true, but just in case
QVERIFY(difference <= callTimer.nsecsElapsed()); // Ideally difference should be 0 exactly
// Make sure setRemainingTime underflows gracefully
deadline.setPreciseRemainingTime(std::numeric_limits<qint64>::min() / 10, 0, timerType);
QVERIFY(!deadline.isForever()); // On Win/macOS the above underflows, make sure we don't saturate to Forever
QVERIFY(deadline.remainingTime() == 0);
// If the timer is saturated we don't want to get a valid number of milliseconds
QVERIFY(deadline.deadline() == std::numeric_limits<qint64>::min());
// Check that the conversion to milliseconds and nanoseconds underflows gracefully
deadline.setPreciseDeadline(std::numeric_limits<qint64>::min() / 10, 0, timerType);
QVERIFY(!deadline.isForever()); // On Win/macOS the above underflows, make sure we don't saturate to Forever
QVERIFY(deadline.deadline() == std::numeric_limits<qint64>::min());
QVERIFY(deadline.deadlineNSecs() == std::numeric_limits<qint64>::min());
}
void tst_QDeadlineTimer::expire()
{
QFETCH_GLOBAL(Qt::TimerType, timerType);

View File

@ -11,6 +11,10 @@
<Incident type="pass" file="" line="0" />
<Duration msecs="0"/>
</TestFunction>
<TestFunction name="wait">
<Incident type="pass" file="" line="0" />
<Duration msecs="0"/>
</TestFunction>
<TestFunction name="cleanupTestCase">
<Incident type="pass" file="" line="0" />
<Duration msecs="0"/>

View File

@ -2,8 +2,9 @@ TAP version 13
# tst_Sleep
ok 1 - initTestCase()
ok 2 - sleep()
ok 3 - cleanupTestCase()
1..3
# tests 3
# pass 3
ok 3 - wait()
ok 4 - cleanupTestCase()
1..4
# tests 4
# pass 4
# fail 0

View File

@ -3,6 +3,8 @@
##teamcity[testFinished name='initTestCase()' flowId='tst_Sleep']
##teamcity[testStarted name='sleep()' flowId='tst_Sleep']
##teamcity[testFinished name='sleep()' flowId='tst_Sleep']
##teamcity[testStarted name='wait()' flowId='tst_Sleep']
##teamcity[testFinished name='wait()' flowId='tst_Sleep']
##teamcity[testStarted name='cleanupTestCase()' flowId='tst_Sleep']
##teamcity[testFinished name='cleanupTestCase()' flowId='tst_Sleep']
##teamcity[testSuiteFinished name='tst_Sleep' flowId='tst_Sleep']

View File

@ -2,6 +2,7 @@
Config: Using QtTest library
PASS : tst_Sleep::initTestCase()
PASS : tst_Sleep::sleep()
PASS : tst_Sleep::wait()
PASS : tst_Sleep::cleanupTestCase()
Totals: 3 passed, 0 failed, 0 skipped, 0 blacklisted, 0ms
Totals: 4 passed, 0 failed, 0 skipped, 0 blacklisted, 0ms
********* Finished testing of tst_Sleep *********

View File

@ -13,6 +13,10 @@
<Incident type="pass" file="" line="0" />
<Duration msecs="0"/>
</TestFunction>
<TestFunction name="wait">
<Incident type="pass" file="" line="0" />
<Duration msecs="0"/>
</TestFunction>
<TestFunction name="cleanupTestCase">
<Incident type="pass" file="" line="0" />
<Duration msecs="0"/>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<testsuite errors="0" failures="0" tests="3" name="tst_Sleep">
<testsuite errors="0" failures="0" tests="4" name="tst_Sleep">
<properties>
<property value="@INSERT_QT_VERSION_HERE@" name="QTestVersion"/>
<property value="@INSERT_QT_VERSION_HERE@" name="QtVersion"/>
@ -7,6 +7,7 @@
</properties>
<testcase result="pass" name="initTestCase"/>
<testcase result="pass" name="sleep"/>
<testcase result="pass" name="wait"/>
<testcase result="pass" name="cleanupTestCase"/>
<system-err/>
</testsuite>

View File

@ -36,6 +36,7 @@ class tst_Sleep: public QObject
private slots:
void sleep();
void wait();
};
void tst_Sleep::sleep()
@ -53,6 +54,24 @@ void tst_Sleep::sleep()
QVERIFY(t.elapsed() > 1000 * 10);
}
void tst_Sleep::wait()
{
QElapsedTimer t;
t.start();
QTest::qWait(1);
QVERIFY(t.elapsed() >= 1);
QTest::qWait(10);
QVERIFY(t.elapsed() >= 11);
QTest::qWait(100);
QVERIFY(t.elapsed() >= 111);
QTest::qWait(1000);
QVERIFY(t.elapsed() >= 1111);
}
QTEST_MAIN(tst_Sleep)
#include "tst_sleep.moc"