From 1ad99391a3d7e2b39664171e034765308c23905c Mon Sep 17 00:00:00 2001 From: Dmitriy Purgin Date: Tue, 11 Feb 2020 22:07:05 +0100 Subject: [PATCH] std::chrono overloads for .timeout() and .delay() (#30) Add convenience overloads accepting durations from the C++ Standard Library. --- docs/qtpromise/qpromise/delay.md | 28 +++++++ docs/qtpromise/qpromise/timeout.md | 36 +++++++++ src/qtpromise/qpromise.h | 10 ++- src/qtpromise/qpromise.inl | 13 +++ .../qtpromise/qpromise/delay/tst_delay.cpp | 43 ++++++++++ .../qpromise/timeout/tst_timeout.cpp | 80 +++++++++++++++++++ 6 files changed, 209 insertions(+), 1 deletion(-) diff --git a/docs/qtpromise/qpromise/delay.md b/docs/qtpromise/qpromise/delay.md index ebf9760..c6e3464 100644 --- a/docs/qtpromise/qpromise/delay.md +++ b/docs/qtpromise/qpromise/delay.md @@ -20,3 +20,31 @@ auto output = input.delay(2000).then([](int res) { // called 2 seconds after `input` is fulfilled }); ``` + +--- + +*Since: 0.6.0* + +```cpp +QPromise::delay(std::chrono::milliseconds msec) -> QPromise +``` + +This is a convenience overload accepting [durations from the C++ Standard Library](https://en.cppreference.com/w/cpp/chrono/duration). + +```cpp +QPromise input = {...} +auto output = input.delay(std::chrono::seconds{2}).then([](int res) { + // called 2 seconds after `input` is fulfilled +}); +``` + +C++14 alternative: + +```cpp +using namespace std::chrono_literals; + +QPromise input = {...} +auto output = input.delay(2s).then([](int res) { + // called 2 seconds after `input` is fulfilled +}); +``` diff --git a/docs/qtpromise/qpromise/timeout.md b/docs/qtpromise/qpromise/timeout.md index 99f3564..1012803 100644 --- a/docs/qtpromise/qpromise/timeout.md +++ b/docs/qtpromise/qpromise/timeout.md @@ -25,3 +25,39 @@ auto output = input.timeout(2000) // operation timed out! }); ``` + +--- + +*Since: 0.6.0* + +```cpp +QPromise::timeout(std::chrono::milliseconds msec, any error = QPromiseTimeoutException) -> QPromise +``` + +This is a convenience overload accepting [durations from the C++ Standard Library](https://en.cppreference.com/w/cpp/chrono/duration). + +```cpp +QPromise input = {...} +auto output = input.timeout(std::chrono::seconds{2}) + .then([](int res) { + // operation succeeded within 2 seconds + }) + .fail([](const QPromiseTimeoutException& error) { + // operation timed out! + }); +``` + +C++14 alternative: + +```cpp +using namespace std::chrono_literals; + +QPromise input = {...} +auto output = input.timeout(2s) + .then([](int res) { + // operation succeeded within 2 seconds + }) + .fail([](const QPromiseTimeoutException& error) { + // operation timed out! + }); +``` diff --git a/src/qtpromise/qpromise.h b/src/qtpromise/qpromise.h index 50959f1..5fb50eb 100644 --- a/src/qtpromise/qpromise.h +++ b/src/qtpromise/qpromise.h @@ -16,6 +16,9 @@ // Qt #include +// C++ Standard Library +#include + namespace QtPromise { template @@ -73,7 +76,12 @@ public: template inline QPromise timeout(int msec, E&& error = E()) const; - inline QPromise delay(int msec) const; + template + inline QPromise timeout(std::chrono::milliseconds msec, E&& error = E()) const; + + inline QPromise delay(int msec) const; + inline QPromise delay(std::chrono::milliseconds msec) const; + inline QPromise wait() const; public: // STATIC diff --git a/src/qtpromise/qpromise.inl b/src/qtpromise/qpromise.inl index 00e76a0..7e0d167 100644 --- a/src/qtpromise/qpromise.inl +++ b/src/qtpromise/qpromise.inl @@ -131,6 +131,13 @@ inline QPromise QPromiseBase::timeout(int msec, E&& error) const }); } +template +template +inline QPromise QPromiseBase::timeout(std::chrono::milliseconds msec, E&& error) const +{ + return timeout(static_cast(msec.count()), std::forward(error)); +} + template inline QPromise QPromiseBase::delay(int msec) const { @@ -141,6 +148,12 @@ inline QPromise QPromiseBase::delay(int msec) const }); } +template +inline QPromise QPromiseBase::delay(std::chrono::milliseconds msec) const +{ + return delay(static_cast(msec.count())); +} + template inline QPromise QPromiseBase::wait() const { diff --git a/tests/auto/qtpromise/qpromise/delay/tst_delay.cpp b/tests/auto/qtpromise/qpromise/delay/tst_delay.cpp index 0dcb777..85c427c 100644 --- a/tests/auto/qtpromise/qpromise/delay/tst_delay.cpp +++ b/tests/auto/qtpromise/qpromise/delay/tst_delay.cpp @@ -13,6 +13,9 @@ // Qt #include +// C++ Standard Library +#include + using namespace QtPromise; class tst_qpromise_delay : public QObject @@ -22,6 +25,9 @@ class tst_qpromise_delay : public QObject private Q_SLOTS: void fulfilled(); void rejected(); + + void fulfilledStdChrono(); + void rejectedStdChrono(); }; QTEST_MAIN(tst_qpromise_delay) @@ -63,3 +69,40 @@ void tst_qpromise_delay::rejected() QCOMPARE(p.isRejected(), true); QVERIFY(elapsed <= 10); } + +void tst_qpromise_delay::fulfilledStdChrono() +{ + QElapsedTimer timer; + qint64 elapsed = -1; + + timer.start(); + + auto p = QPromise::resolve(42).delay(std::chrono::seconds{1}).finally([&]() { + elapsed = timer.elapsed(); + }); + + QCOMPARE(waitForValue(p, -1), 42); + QCOMPARE(p.isFulfilled(), true); + + // Qt::CoarseTimer (default) Coarse timers try to + // keep accuracy within 5% of the desired interval. + // Require accuracy within 6% for passing the test. + QVERIFY(elapsed >= static_cast(1000 * 0.94)); + QVERIFY(elapsed <= static_cast(1000 * 1.06)); +} + +void tst_qpromise_delay::rejectedStdChrono() +{ + QElapsedTimer timer; + qint64 elapsed = -1; + + timer.start(); + + auto p = QPromise::reject(QString("foo")).delay(std::chrono::seconds{1}).finally([&]() { + elapsed = timer.elapsed(); + }); + + QCOMPARE(waitForError(p, QString()), QString("foo")); + QCOMPARE(p.isRejected(), true); + QVERIFY(elapsed <= 10); +} diff --git a/tests/auto/qtpromise/qpromise/timeout/tst_timeout.cpp b/tests/auto/qtpromise/qpromise/timeout/tst_timeout.cpp index a264d64..f6da085 100644 --- a/tests/auto/qtpromise/qpromise/timeout/tst_timeout.cpp +++ b/tests/auto/qtpromise/qpromise/timeout/tst_timeout.cpp @@ -13,6 +13,9 @@ // Qt #include +// C++ Standard Library +#include + using namespace QtPromise; class tst_qpromise_timeout : public QObject @@ -23,6 +26,10 @@ private Q_SLOTS: void fulfilled(); void rejected(); void timeout(); + + void fulfilledStdChrono(); + void rejectedStdChrono(); + void timeoutStdChrono(); }; QTEST_MAIN(tst_qpromise_timeout) @@ -100,3 +107,76 @@ void tst_qpromise_timeout::timeout() QVERIFY(elapsed >= static_cast(2000 * 0.94)); QVERIFY(elapsed <= static_cast(2000 * 1.06)); } + +void tst_qpromise_timeout::fulfilledStdChrono() +{ + QElapsedTimer timer; + qint64 elapsed = -1; + + timer.start(); + + auto p = QPromise([](const QPromiseResolve& resolve) { + QTimer::singleShot(1000, [=]() { + resolve(42); + }); + }).timeout(std::chrono::seconds{2}).finally([&]() { + elapsed = timer.elapsed(); + }); + + QCOMPARE(waitForValue(p, -1), 42); + QCOMPARE(p.isFulfilled(), true); + QVERIFY(elapsed < 2000); +} + +void tst_qpromise_timeout::rejectedStdChrono() +{ + QElapsedTimer timer; + qint64 elapsed = -1; + + timer.start(); + + auto p = QPromise([](const QPromiseResolve&, const QPromiseReject& reject) { + QTimer::singleShot(1000, [=]() { + reject(QString("foo")); + }); + }).timeout(std::chrono::seconds{2}).finally([&]() { + elapsed = timer.elapsed(); + }); + + + QCOMPARE(waitForError(p, QString()), QString("foo")); + QCOMPARE(p.isRejected(), true); + QVERIFY(elapsed < 2000); +} + +void tst_qpromise_timeout::timeoutStdChrono() +{ + QElapsedTimer timer; + qint64 elapsed = -1; + bool failed = false; + + timer.start(); + + auto p = QPromise([](const QPromiseResolve& resolve) { + QTimer::singleShot(4000, [=]() { + resolve(42); + }); + }).timeout(std::chrono::seconds{2}).finally([&]() { + elapsed = timer.elapsed(); + }); + + p.fail([&](const QPromiseTimeoutException&) { + failed = true; + return -1; + }).wait(); + + QCOMPARE(waitForValue(p, -1), -1); + QCOMPARE(p.isRejected(), true); + QCOMPARE(failed, true); + + // Qt::CoarseTimer (default) Coarse timers try to + // keep accuracy within 5% of the desired interval. + // Require accuracy within 6% for passing the test. + QVERIFY(elapsed >= static_cast(2000 * 0.94)); + QVERIFY(elapsed <= static_cast(2000 * 1.06)); +}