From 0f78e85421de113d73edf0d58b34e12ad188417c Mon Sep 17 00:00:00 2001 From: Sona Kurazyan Date: Mon, 30 Mar 2020 10:46:00 +0200 Subject: Add support of failure handler callbacks to QFuture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added QFuture::onFailed() method, which allows attaching handlers for exceptions that may occur in QFuture continuation chains. Task-number: QTBUG-81588 Change-Id: Iadeee99e3a7573207f6ca9f650ff9f7b6faa2cf7 Reviewed-by: MÃ¥rten Nordheim --- tests/auto/corelib/thread/qfuture/tst_qfuture.cpp | 333 ++++++++++++++++++++++ 1 file changed, 333 insertions(+) (limited to 'tests/auto/corelib') diff --git a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp index 4e20741055..1caa386638 100644 --- a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp +++ b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp @@ -104,6 +104,8 @@ private slots: #ifndef QT_NO_EXCEPTIONS void thenOnExceptionFuture(); void thenThrows(); + void onFailed(); + void onFailedTestCallables(); #endif void takeResults(); void takeResult(); @@ -2149,6 +2151,337 @@ void tst_QFuture::thenThrows() } } +void tst_QFuture::onFailed() +{ + // Ready exception void future + { + int checkpoint = 0; + auto future = createExceptionFuture().then([&] { checkpoint = 1; }).onFailed([&] { + checkpoint = 2; + }); + + try { + future.waitForFinished(); + } catch (...) { + checkpoint = 3; + } + QCOMPARE(checkpoint, 2); + } + + // std::exception handler + { + QFutureInterface promise; + + int checkpoint = 0; + auto then = promise.future() + .then([&](int res) { + throw std::exception(); + return res; + }) + .then([&](int res) { return res + 1; }) + .onFailed([&](const QException &) { + checkpoint = 1; + return -1; + }) + .onFailed([&](const std::exception &) { + checkpoint = 2; + return -1; + }) + .onFailed([&] { + checkpoint = 3; + return -1; + }); + + promise.reportStarted(); + promise.reportResult(1); + promise.reportFinished(); + + int res = 0; + try { + res = then.result(); + } catch (...) { + checkpoint = 4; + } + QCOMPARE(checkpoint, 2); + QCOMPARE(res, -1); + } + + // then() throws an exception derived from QException + { + QFutureInterface promise; + + int checkpoint = 0; + auto then = promise.future() + .then([&](int res) { + throw DerivedException(); + return res; + }) + .then([&](int res) { return res + 1; }) + .onFailed([&](const QException &) { + checkpoint = 1; + return -1; + }) + .onFailed([&](const std::exception &) { + checkpoint = 2; + return -1; + }) + .onFailed([&] { + checkpoint = 3; + return -1; + }); + + promise.reportStarted(); + promise.reportResult(1); + promise.reportFinished(); + + int res = 0; + try { + res = then.result(); + } catch (...) { + checkpoint = 4; + } + QCOMPARE(checkpoint, 1); + QCOMPARE(res, -1); + } + + // then() throws a custom exception + { + QFutureInterface promise; + + int checkpoint = 0; + auto then = promise.future() + .then([&](int res) { + throw TestException(); + return res; + }) + .then([&](int res) { return res + 1; }) + .onFailed([&](const QException &) { + checkpoint = 1; + return -1; + }) + .onFailed([&](const std::exception &) { + checkpoint = 2; + return -1; + }) + .onFailed([&] { + checkpoint = 3; + return -1; + }); + + promise.reportStarted(); + promise.reportResult(1); + promise.reportFinished(); + + int res = 0; + try { + res = then.result(); + } catch (...) { + checkpoint = 4; + } + QCOMPARE(checkpoint, 3); + QCOMPARE(res, -1); + } + + // Custom exception handler + { + struct TestException + { + }; + + QFutureInterface promise; + + int checkpoint = 0; + auto then = promise.future() + .then([&](int res) { + throw TestException(); + return res; + }) + .then([&](int res) { return res + 1; }) + .onFailed([&](const QException &) { + checkpoint = 1; + return -1; + }) + .onFailed([&](const TestException &) { + checkpoint = 2; + return -1; + }) + .onFailed([&] { + checkpoint = 3; + return -1; + }); + + promise.reportStarted(); + promise.reportResult(1); + promise.reportFinished(); + + int res = 0; + try { + res = then.result(); + } catch (...) { + checkpoint = 4; + } + QCOMPARE(checkpoint, 2); + QCOMPARE(res, -1); + } + + // Handle all exceptions + { + QFutureInterface promise; + + int checkpoint = 0; + auto then = promise.future() + .then([&](int res) { + throw QException(); + return res; + }) + .then([&](int res) { return res + 1; }) + .onFailed([&] { + checkpoint = 1; + return -1; + }) + .onFailed([&](const QException &) { + checkpoint = 2; + return -1; + }); + + promise.reportStarted(); + promise.reportResult(1); + promise.reportFinished(); + + int res = 0; + try { + res = then.result(); + } catch (...) { + checkpoint = 3; + } + QCOMPARE(checkpoint, 1); + QCOMPARE(res, -1); + } + + // Handler throws exception + { + QFutureInterface promise; + + int checkpoint = 0; + auto then = promise.future() + .then([&](int res) { + throw QException(); + return res; + }) + .then([&](int res) { return res + 1; }) + .onFailed([&](const QException &) { + checkpoint = 1; + throw QException(); + return -1; + }) + .onFailed([&] { + checkpoint = 2; + return -1; + }); + + promise.reportStarted(); + promise.reportResult(1); + promise.reportFinished(); + + int res = 0; + try { + res = then.result(); + } catch (...) { + checkpoint = 3; + } + QCOMPARE(checkpoint, 2); + QCOMPARE(res, -1); + } + + // No handler for exception + { + QFutureInterface promise; + + int checkpoint = 0; + auto then = promise.future() + .then([&](int res) { + throw QException(); + return res; + }) + .then([&](int res) { return res + 1; }) + .onFailed([&](const std::exception &) { + checkpoint = 1; + throw std::exception(); + return -1; + }) + .onFailed([&](QException &) { + checkpoint = 2; + return -1; + }); + + promise.reportStarted(); + promise.reportResult(1); + promise.reportFinished(); + + int res = 0; + try { + res = then.result(); + } catch (...) { + checkpoint = 3; + } + QCOMPARE(checkpoint, 3); + QCOMPARE(res, 0); + } +} + +template +bool runForCallable(Callable &&handler) +{ + QFuture future = createExceptionResultFuture() + .then([&](int) { return 1; }) + .onFailed(std::forward(handler)); + + int res = 0; + try { + res = future.result(); + } catch (...) { + return false; + } + return res == -1; +} + +int foo() +{ + return -1; +} + +void tst_QFuture::onFailedTestCallables() +{ + QVERIFY(runForCallable([&] { return -1; })); + QVERIFY(runForCallable(foo)); + QVERIFY(runForCallable(&foo)); + + std::function func = foo; + QVERIFY(runForCallable(func)); + + struct Functor1 + { + int operator()() { return -1; } + static int foo() { return -1; } + }; + QVERIFY(runForCallable(Functor1())); + QVERIFY(runForCallable(Functor1::foo)); + + struct Functor2 + { + int operator()() const { return -1; } + static int foo() { return -1; } + }; + QVERIFY(runForCallable(Functor2())); + + struct Functor3 + { + int operator()() const noexcept { return -1; } + static int foo() { return -1; } + }; + QVERIFY(runForCallable(Functor3())); +} + #endif // QT_NO_EXCEPTIONS void tst_QFuture::testSingleResult(const UniquePtr &p) -- cgit v1.2.3