8 Commits

Author SHA1 Message Date
5523597e7c - wip -
- wip -
2018-05-10 11:51:54 +02:00
26a2110a14 Fix GCC -Wold-style-cast warnings
Explicitly enable -Wold-style-cast and make all warnings into errors in tests (-Werror on GCC, -WX on MSVC).
2018-05-10 11:51:17 +02:00
fa987a5044 Cleanup promise captured in resolve/reject
Make sure that QPromiseResolve and QPromiseReject release their associated promise as soon as one of them is resolved or rejected, ensuring that the promise data is cleaned up once resolved (that fixes cases where one or both of them are captured in a signal/slot connection lambda)
2018-05-10 11:51:17 +02:00
7b0cba5b9d Allow QSharedPointer as rejection reason
Embed the promise fulfillment value and rejection reason in respectively PromiseValue and PromiseError private wrappers, both storing the data in a shared pointer (QPromiseError is now deprecated).
2018-05-10 11:51:14 +02:00
2c8ed6e676 Remove extra space between closing angle brackets
The extra space between template closing angle brackets is not anymore required in C++11 (https://en.wikipedia.org/wiki/C%2B%2B11#Right_angle_bracket)
2018-05-10 09:21:21 +02:00
d128a5fa8d Clarify QPromise::all fulfillment values order 2018-04-30 19:05:21 +02:00
dcbb2ef860 Fix clang "unused type alias 'FType'" warning 2018-04-26 16:06:10 +02:00
50bae380be Implement QPromise::tapFail(handler) 2018-03-29 09:08:19 +02:00
29 changed files with 930 additions and 277 deletions

View File

@ -30,7 +30,3 @@ script:
after_success:
- bash <(curl -s https://codecov.io/bash) -f coverage.info
# gitbook install .
# gitbook build . dist/docs

View File

@ -11,6 +11,7 @@
* [.isPending](qtpromise/qpromise/ispending.md)
* [.isRejected](qtpromise/qpromise/isrejected.md)
* [.tap](qtpromise/qpromise/tap.md)
* [.tapFail](qtpromise/qpromise/tapfail.md)
* [.then](qtpromise/qpromise/then.md)
* [.timeout](qtpromise/qpromise/timeout.md)
* [.wait](qtpromise/qpromise/wait.md)

View File

@ -10,6 +10,7 @@
* [`QPromise<T>::isPending`](qpromise/ispending.md)
* [`QPromise<T>::isRejected`](qpromise/isrejected.md)
* [`QPromise<T>::tap`](qpromise/tap.md)
* [`QPromise<T>::tapFail`](qpromise/tapfail.md)
* [`QPromise<T>::then`](qpromise/then.md)
* [`QPromise<T>::timeout`](qpromise/timeout.md)
* [`QPromise<T>::wait`](qpromise/wait.md)

View File

@ -4,7 +4,9 @@
[static] QPromise<T>::all(Sequence<QPromise<T>> promises) -> QPromise<QVector<T>>
```
Returns a `QPromise<QVector<T>>` that fulfills when **all** `promises` of (the same) type `T` have been fulfilled. The `output` value is a vector containing **all** the values of `promises`, in the same order. If any of the given `promises` fail, `output` immediately rejects with the error of the promise that rejected, whether or not the other promises are resolved.
Returns a `QPromise<QVector<T>>` that fulfills when **all** `promises` of (the same) type `T` have been fulfilled. The `output` value is a vector containing all the values of `promises`, in the same order, i.e., at the respective positions to the original sequence, regardless of completion order.
If any of the given `promises` fail, `output` immediately rejects with the error of the promise that rejected, whether or not the other promises are resolved.
`Sequence` is any STL compatible container (eg. `QVector`, `QList`, `std::vector`, etc.)

View File

@ -0,0 +1,21 @@
## `QPromise<T>::tapFail`
```
QPromise<T>::tapFail(Function handler) -> QPromise<T>
```
This `handler` allows to observe errors of the `input` promise without handling them - similar to [`finally`](finally.md) but **only** called on rejections. The `output` promise has the same type as the `input` one but also the same value or error. However, if `handler` throws, `output` is rejected with the new exception.
```cpp
QPromise<int> input = {...}
auto output = input.tapFail([](Error err) {
log(err);
}).then([](int res) {
return process(res);
}).fail([](Error err) {
handle(err);
return -1;
});
```
If `handler` returns a promise (or QFuture), the `output` promise is delayed until the returned promise is resolved and under the same conditions: the delayed value is ignored, the error transmitted to the `output` promise.

View File

@ -3,6 +3,7 @@
// QtPromise
#include "qpromise_p.h"
#include "qpromiseerror.h"
#include "qpromiseglobal.h"
// Qt
@ -22,6 +23,9 @@ public:
template <typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count != 1, int>::type = 0>
inline QPromiseBase(F resolver);
template <typename U>
inline QPromiseBase(const QPromise<U>& other);
QPromiseBase(const QPromiseBase<T>& other): m_d(other.m_d) {}
QPromiseBase(const QPromise<T>& other): m_d(other.m_d) {}
QPromiseBase(QPromiseBase<T>&& other) Q_DECL_NOEXCEPT { swap(other); }
@ -59,6 +63,9 @@ public:
template <typename THandler>
inline QPromise<T> tap(THandler handler) const;
template <typename THandler>
inline QPromise<T> tapFail(THandler handler) const;
template <typename E = QPromiseTimeoutException>
inline QPromise<T> timeout(int msec, E&& error = E()) const;
@ -70,11 +77,10 @@ public: // STATIC
inline static QPromise<T> reject(E&& error);
protected:
friend struct QtPromisePrivate::PromiseFulfill<QPromise<T> >;
friend class QPromiseResolve<T>;
friend class QPromiseReject<T>;
friend struct QtPromisePrivate::PromiseFulfill<QPromise<T>>;
friend class QtPromisePrivate::PromiseResolver<T>;
QExplicitlySharedDataPointer<QtPromisePrivate::PromiseData<T> > m_d;
QExplicitlySharedDataPointer<QtPromisePrivate::PromiseData<T>> m_d;
};
template <typename T>
@ -86,11 +92,14 @@ public:
public: // STATIC
template <template <typename, typename...> class Sequence = QVector, typename ...Args>
inline static QPromise<QVector<T> > all(const Sequence<QPromise<T>, Args...>& promises);
inline static QPromise<QVector<T>> all(const Sequence<QPromise<T>, Args...>& promises);
inline static QPromise<T> resolve(const T& value);
inline static QPromise<T> resolve(T&& value);
template <typename U>
operator QPromise<U>();
private:
friend class QPromiseBase<T>;
};
@ -102,6 +111,9 @@ public:
template <typename F>
QPromise(F&& resolver): QPromiseBase<void>(std::forward<F>(resolver)) { }
template <typename T>
QPromise(const QPromise<T>& other);
public: // STATIC
template <template <typename, typename...> class Sequence = QVector, typename ...Args>
inline static QPromise<void> all(const Sequence<QPromise<void>, Args...>& promises);

View File

@ -9,95 +9,82 @@ template <class T>
class QPromiseResolve
{
public:
QPromiseResolve(QPromise<T> p)
: m_promise(new QPromise<T>(std::move(p)))
QPromiseResolve(QtPromisePrivate::PromiseResolver<T> resolver)
: m_resolver(std::move(resolver))
{ }
template <typename V>
void operator()(V&& value) const
{
Q_ASSERT(!m_promise.isNull());
if (m_promise->isPending()) {
m_promise->m_d->resolve(std::forward<V>(value));
m_promise->m_d->dispatch();
}
m_resolver.resolve(std::forward<V>(value));
}
private:
QSharedPointer<QPromise<T> > m_promise;
};
template <>
class QPromiseResolve<void>
{
public:
QPromiseResolve(QPromise<void> p)
: m_promise(new QPromise<void>(std::move(p)))
{ }
void operator()() const
{
Q_ASSERT(!m_promise.isNull());
if (m_promise->isPending()) {
m_promise->m_d->resolve();
m_promise->m_d->dispatch();
}
m_resolver.resolve();
}
private:
QSharedPointer<QPromise<void> > m_promise;
mutable QtPromisePrivate::PromiseResolver<T> m_resolver;
};
template <class T>
class QPromiseReject
{
public:
QPromiseReject(QPromise<T> p)
: m_promise(new QPromise<T>(std::move(p)))
QPromiseReject(QtPromisePrivate::PromiseResolver<T> resolver)
: m_resolver(std::move(resolver))
{ }
template <typename E>
void operator()(E&& error) const
{
Q_ASSERT(!m_promise.isNull());
if (m_promise->isPending()) {
m_promise->m_d->reject(std::forward<E>(error));
m_promise->m_d->dispatch();
}
m_resolver.reject(std::forward<E>(error));
}
private:
QSharedPointer<QPromise<T> > m_promise;
mutable QtPromisePrivate::PromiseResolver<T> m_resolver;
};
template <typename T>
template <typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count == 1, int>::type>
inline QPromiseBase<T>::QPromiseBase(F resolver)
inline QPromiseBase<T>::QPromiseBase(F callback)
: m_d(new QtPromisePrivate::PromiseData<T>())
{
QPromiseResolve<T> resolve(*this);
QPromiseReject<T> reject(*this);
QtPromisePrivate::PromiseResolver<T> resolver(*this);
try {
resolver(resolve);
callback(QPromiseResolve<T>(resolver));
} catch (...) {
reject(std::current_exception());
resolver.reject(std::current_exception());
}
}
template <typename T>
template <typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count != 1, int>::type>
inline QPromiseBase<T>::QPromiseBase(F resolver)
inline QPromiseBase<T>::QPromiseBase(F callback)
: m_d(new QtPromisePrivate::PromiseData<T>())
{
QtPromisePrivate::PromiseResolver<T> resolver(*this);
try {
callback(QPromiseResolve<T>(resolver), QPromiseReject<T>(resolver));
} catch (...) {
resolver.reject(std::current_exception());
}
}
template <typename T>
template <typename U>
inline QPromiseBase<T>::QPromiseBase(const QPromise<U>& other)
: m_d(new QtPromisePrivate::PromiseData<T>())
{
using namespace QtPromisePrivate;
QPromiseResolve<T> resolve(*this);
QPromiseReject<T> reject(*this);
try {
resolver(resolve, reject);
} catch (...) {
reject(std::current_exception());
}
PromiseFulfill<QPromise<U> >::call(other, PromiseCast<U, T>::apply(resolve), reject);
}
template <typename T>
@ -158,6 +145,16 @@ inline QPromise<T> QPromiseBase<T>::tap(THandler handler) const
});
}
template <typename T>
template <typename THandler>
inline QPromise<T> QPromiseBase<T>::tapFail(THandler handler) const
{
QPromise<T> p = *this;
return p.then([](){}, handler).then([=]() {
return p;
});
}
template <typename T>
template <typename E>
inline QPromise<T> QPromiseBase<T>::timeout(int msec, E&& error) const
@ -174,7 +171,7 @@ inline QPromise<T> QPromiseBase<T>::timeout(int msec, E&& error) const
reject(std::move(error));
});
QtPromisePrivate::PromiseFulfill<QPromise<T> >::call(p, resolve, reject);
QtPromisePrivate::PromiseFulfill<QPromise<T>>::call(p, resolve, reject);
});
}
@ -211,19 +208,19 @@ inline QPromise<T> QPromiseBase<T>::reject(E&& error)
template <typename T>
template <template <typename, typename...> class Sequence, typename ...Args>
inline QPromise<QVector<T> > QPromise<T>::all(const Sequence<QPromise<T>, Args...>& promises)
inline QPromise<QVector<T>> QPromise<T>::all(const Sequence<QPromise<T>, Args...>& promises)
{
const int count = (int)promises.size();
const int count = static_cast<int>(promises.size());
if (count == 0) {
return QPromise<QVector<T> >::resolve({});
return QPromise<QVector<T>>::resolve({});
}
return QPromise<QVector<T> >([=](
const QPromiseResolve<QVector<T> >& resolve,
const QPromiseReject<QVector<T> >& reject) {
return QPromise<QVector<T>>([=](
const QPromiseResolve<QVector<T>>& resolve,
const QPromiseReject<QVector<T>>& reject) {
QSharedPointer<int> remaining(new int(count));
QSharedPointer<QVector<T> > results(new QVector<T>(count));
QSharedPointer<QVector<T>> results(new QVector<T>(count));
int i = 0;
for (const auto& promise: promises) {
@ -260,10 +257,25 @@ inline QPromise<T> QPromise<T>::resolve(T&& value)
});
}
template <typename T>
template <typename U>
QPromise<T>::operator QPromise<U>()
{
return QPromise<U>::resolve(U());
}
template <typename T>
QPromise<void>::QPromise(const QPromise<T>& other)
: QPromiseBase<void>([&](const QPromiseResolve<void>& resolve, const QPromiseReject<void>& reject) {
QtPromisePrivate::PromiseFulfill<QPromise<T> >::call(other, resolve, reject);
})
{
}
template <template <typename, typename...> class Sequence, typename ...Args>
inline QPromise<void> QPromise<void>::all(const Sequence<QPromise<void>, Args...>& promises)
{
const int count = (int)promises.size();
const int count = static_cast<int>(promises.size());
if (count == 0) {
return QPromise<void>::resolve();
}

View File

@ -2,7 +2,6 @@
#define QTPROMISE_QPROMISE_P_H
// QtPromise
#include "qpromiseerror.h"
#include "qpromiseglobal.h"
// Qt
@ -34,9 +33,10 @@ namespace QtPromisePrivate {
template <typename F>
static void qtpromise_defer(F&& f, const QPointer<QThread>& thread)
{
using FType = typename std::decay<F>::type;
struct Event : public QEvent
{
using FType = typename std::decay<F>::type;
Event(FType&& f) : QEvent(QEvent::None), m_f(std::move(f)) { }
Event(const FType& f) : QEvent(QEvent::None), m_f(f) { }
~Event() { m_f(); }
@ -72,35 +72,71 @@ static void qtpromise_defer(F&& f)
}
template <typename T>
struct PromiseDeduce
class PromiseValue
{
using Type = QtPromise::QPromise<Unqualified<T> >;
public:
PromiseValue() { }
PromiseValue(const T& data) : m_data(new T(data)) { }
PromiseValue(T&& data) : m_data(new T(std::move(data))) { }
bool isNull() const { return m_data.isNull(); }
const T& data() const { return *m_data; }
private:
QSharedPointer<T> m_data;
};
class PromiseError
{
public:
template <typename T>
PromiseError(const T& value)
{
try {
throw value;
} catch (...) {
m_data = std::current_exception();
}
}
PromiseError() { }
PromiseError(const std::exception_ptr& exception) : m_data(exception) { }
void rethrow() const { std::rethrow_exception(m_data); }
bool isNull() const { return m_data == nullptr; }
private:
// NOTE(SB) std::exception_ptr is already a shared pointer
std::exception_ptr m_data;
};
template <typename T>
struct PromiseDeduce<QtPromise::QPromise<T> >
struct PromiseDeduce
{
using Type = QtPromise::QPromise<Unqualified<T>>;
};
template <typename T>
struct PromiseDeduce<QtPromise::QPromise<T>>
: public PromiseDeduce<T>
{ };
template <typename T>
struct PromiseFulfill
{
static void call(
T&& value,
const QtPromise::QPromiseResolve<T>& resolve,
const QtPromise::QPromiseReject<T>&)
template <typename TResolve, typename TReject>
static void call(T&& value, const TResolve& resolve, const TReject&)
{
resolve(std::move(value));
}
};
template <typename T>
struct PromiseFulfill<QtPromise::QPromise<T> >
struct PromiseFulfill<QtPromise::QPromise<T>>
{
template <typename TResolve, typename TReject>
static void call(
const QtPromise::QPromise<T>& promise,
const QtPromise::QPromiseResolve<T>& resolve,
const QtPromise::QPromiseReject<T>& reject)
const TResolve& resolve,
const TReject& reject)
{
if (promise.isFulfilled()) {
resolve(promise.m_d->value());
@ -117,11 +153,11 @@ struct PromiseFulfill<QtPromise::QPromise<T> >
};
template <>
struct PromiseFulfill<QtPromise::QPromise<void> >
struct PromiseFulfill<QtPromise::QPromise<void>>
{
template <typename TPromise, typename TResolve, typename TReject>
template <typename TResolve, typename TReject>
static void call(
const TPromise& promise,
const QtPromise::QPromise<void>& promise,
const TResolve& resolve,
const TReject& reject)
{
@ -305,12 +341,12 @@ struct PromiseCatcher
using ResType = typename std::result_of<THandler(TArg)>::type;
template <typename TResolve, typename TReject>
static std::function<void(const QtPromise::QPromiseError&)> create(
static std::function<void(const PromiseError&)> create(
const THandler& handler,
const TResolve& resolve,
const TReject& reject)
{
return [=](const QtPromise::QPromiseError& error) {
return [=](const PromiseError& error) {
try {
error.rethrow();
} catch (const TArg& error) {
@ -328,12 +364,12 @@ struct PromiseCatcher<T, THandler, void>
using ResType = typename std::result_of<THandler()>::type;
template <typename TResolve, typename TReject>
static std::function<void(const QtPromise::QPromiseError&)> create(
static std::function<void(const PromiseError&)> create(
const THandler& handler,
const TResolve& resolve,
const TReject& reject)
{
return [=](const QtPromise::QPromiseError& error) {
return [=](const PromiseError& error) {
try {
error.rethrow();
} catch (...) {
@ -347,12 +383,12 @@ template <typename T>
struct PromiseCatcher<T, std::nullptr_t, void>
{
template <typename TResolve, typename TReject>
static std::function<void(const QtPromise::QPromiseError&)> create(
static std::function<void(const PromiseError&)> create(
std::nullptr_t,
const TResolve&,
const TReject& reject)
{
return [=](const QtPromise::QPromiseError& error) {
return [=](const PromiseError& error) {
// 2.2.7.4. If onRejected is not a function and promise1 is rejected,
// promise2 must be rejected with the same reason as promise1
reject(error);
@ -360,15 +396,75 @@ struct PromiseCatcher<T, std::nullptr_t, void>
}
};
template <typename T, typename U>
struct PromiseCast
{
template <typename F>
static auto apply(const F& resolve)
{
return [=](const QSharedPointer<T>& value) {
resolve(static_cast<T>(*value));
};
}
};
template <typename T>
struct PromiseCast<T, void>
{
template <typename F>
static auto apply(const F& resolve)
{
return [=](const QSharedPointer<T>&) {
resolve();
};
}
};
/*
template <typename T>
struct PromiseCast<T, QVariant>
{
static QtPromise::QPromise<QVariant> cast(
const QtPromise::QPromiseBase<T>& input)
{
return input.then([](const T& res) {
return QVariant::fromValue(res);
});
}
};
template <typename U>
struct PromiseCast<QVariant, U>
{
static QtPromise::QPromise<U> cast(
const QtPromise::QPromiseBase<QVariant>& input)
{
return input.then([](const QVariant& res) {
return res.value<U>();
});
}
};
template <>
struct PromiseCast<void, QVariant>
{
static QtPromise::QPromise<void> cast(
const QtPromise::QPromiseBase<QVariant>& input)
{
return input.then([]() {
});
}
};
*/
template <typename T> class PromiseData;
template <typename T, typename F>
class PromiseDataBase : public QSharedData
{
public:
using Error = QtPromise::QPromiseError;
using Handler = std::pair<QPointer<QThread>, std::function<F> >;
using Catcher = std::pair<QPointer<QThread>, std::function<void(const Error&)> >;
using Handler = std::pair<QPointer<QThread>, std::function<F>>;
using Catcher = std::pair<QPointer<QThread>, std::function<void(const PromiseError&)>>;
virtual ~PromiseDataBase() {}
@ -394,29 +490,22 @@ public:
m_handlers.append({QThread::currentThread(), std::move(handler)});
}
void addCatcher(std::function<void(const Error&)> catcher)
void addCatcher(std::function<void(const PromiseError&)> catcher)
{
QWriteLocker lock(&m_lock);
m_catchers.append({QThread::currentThread(), std::move(catcher)});
}
void reject(Error error)
template <typename E>
void reject(E&& error)
{
Q_ASSERT(isPending());
Q_ASSERT(m_error.isNull());
m_error.reset(new Error(std::move(error)));
m_error = PromiseError(std::forward<E>(error));
setSettled();
}
void reject(const QSharedPointer<Error>& error)
{
Q_ASSERT(isPending());
Q_ASSERT(m_error.isNull());
m_error = error;
this->setSettled();
}
const QSharedPointer<Error>& error() const
const PromiseError& error() const
{
Q_ASSERT(isRejected());
return m_error;
@ -445,13 +534,13 @@ public:
return;
}
QSharedPointer<Error> error = m_error;
PromiseError error(m_error);
Q_ASSERT(!error.isNull());
for (const auto& catcher: catchers) {
const auto& fn = catcher.second;
qtpromise_defer([=]() {
fn(*error);
fn(error);
}, catcher.first);
}
}
@ -472,7 +561,7 @@ private:
bool m_settled = false;
QVector<Handler> m_handlers;
QVector<Catcher> m_catchers;
QSharedPointer<Error> m_error;
PromiseError m_error;
};
template <typename T>
@ -481,31 +570,16 @@ class PromiseData : public PromiseDataBase<T, void(const T&)>
using Handler = typename PromiseDataBase<T, void(const T&)>::Handler;
public:
void resolve(T&& value)
template <typename V>
void resolve(V&& value)
{
Q_ASSERT(this->isPending());
Q_ASSERT(m_value.isNull());
m_value.reset(new T(std::move(value)));
m_value = PromiseValue<T>(std::forward<V>(value));
this->setSettled();
}
void resolve(const T& value)
{
Q_ASSERT(this->isPending());
Q_ASSERT(m_value.isNull());
m_value.reset(new T(value));
this->setSettled();
}
void resolve(const QSharedPointer<T>& value)
{
Q_ASSERT(this->isPending());
Q_ASSERT(m_value.isNull());
m_value = value;
this->setSettled();
}
const QSharedPointer<T>& value() const
const PromiseValue<T>& value() const
{
Q_ASSERT(this->isFulfilled());
return m_value;
@ -513,19 +587,19 @@ public:
void notify(const QVector<Handler>& handlers) Q_DECL_OVERRIDE
{
QSharedPointer<T> value(m_value);
PromiseValue<T> value(m_value);
Q_ASSERT(!value.isNull());
for (const auto& handler: handlers) {
const auto& fn = handler.second;
qtpromise_defer([=]() {
fn(*value);
fn(value.data());
}, handler.first);
}
}
private:
QSharedPointer<T> m_value;
PromiseValue<T> m_value;
};
template <>
@ -548,6 +622,68 @@ protected:
}
};
template <typename T>
class PromiseResolver
{
public:
PromiseResolver(QtPromise::QPromise<T> promise)
: m_d(new Data())
{
m_d->promise = new QtPromise::QPromise<T>(std::move(promise));
}
template <typename E>
void reject(E&& error)
{
auto promise = m_d->promise;
if (promise) {
Q_ASSERT(promise->isPending());
promise->m_d->reject(std::forward<E>(error));
promise->m_d->dispatch();
release();
}
}
template <typename V>
void resolve(V&& value)
{
auto promise = m_d->promise;
if (promise) {
Q_ASSERT(promise->isPending());
promise->m_d->resolve(std::forward<V>(value));
promise->m_d->dispatch();
release();
}
}
void resolve()
{
auto promise = m_d->promise;
if (promise) {
Q_ASSERT(promise->isPending());
promise->m_d->resolve();
promise->m_d->dispatch();
release();
}
}
private:
struct Data : public QSharedData
{
QtPromise::QPromise<T>* promise = nullptr;
};
QExplicitlySharedDataPointer<Data> m_d;
void release()
{
Q_ASSERT(m_d->promise);
Q_ASSERT(!m_d->promise->isPending());
delete m_d->promise;
m_d->promise = nullptr;
}
};
} // namespace QtPromise
#endif // ifndef QTPROMISE_QPROMISE_H
#endif // ifndef QTPROMISE_QPROMISE_P_H

View File

@ -2,6 +2,7 @@
#define QTPROMISE_QPROMISEERROR_H
// QtPromise
#include "qpromise_p.h"
#include "qpromiseglobal.h"
// Qt
@ -9,53 +10,6 @@
namespace QtPromise {
class QPromiseError
{
public:
template <typename T>
QPromiseError(const T& value)
{
try {
throw value;
} catch (...) {
m_exception = std::current_exception();
}
}
QPromiseError(const std::exception_ptr& exception)
: m_exception(exception)
{ }
QPromiseError(const QPromiseError& error)
: m_exception(error.m_exception)
{ }
QPromiseError(QPromiseError&& other)
: m_exception(nullptr)
{
swap(other);
}
QPromiseError& operator=(QPromiseError other)
{
swap(other);
return *this;
}
void swap(QPromiseError& other)
{
qSwap(m_exception, other.m_exception);
}
void rethrow() const
{
std::rethrow_exception(m_exception);
}
private:
std::exception_ptr m_exception;
};
class QPromiseTimeoutException : public QException
{
public:
@ -66,6 +20,12 @@ public:
}
};
// QPromiseError is provided for backward compatibility and will be
// removed in the next major version: it wasn't intended to be used
// directly and thus should not be part of the public API.
// TODO Remove QPromiseError at version 1.0
using QPromiseError = QtPromisePrivate::PromiseError;
} // namespace QtPromise
#endif // QTPROMISE_QPROMISEERROR_H

View File

@ -22,12 +22,12 @@ public:
namespace QtPromisePrivate {
template <typename T>
struct PromiseDeduce<QFuture<T> >
struct PromiseDeduce<QFuture<T>>
: public PromiseDeduce<T>
{ };
template <typename T>
struct PromiseFulfill<QFuture<T> >
struct PromiseFulfill<QFuture<T>>
{
static void call(
const QFuture<T>& future,
@ -62,7 +62,7 @@ struct PromiseFulfill<QFuture<T> >
};
template <>
struct PromiseFulfill<QFuture<void> >
struct PromiseFulfill<QFuture<void>>
{
static void call(
const QFuture<void>& future,

View File

@ -85,7 +85,7 @@ struct ArgsOf<TReturn(T::*)(Args...) const volatile> : public ArgsTraits<Args...
{ };
template <typename T>
struct ArgsOf<std::function<T> > : public ArgsOf<T>
struct ArgsOf<std::function<T>> : public ArgsOf<T>
{ };
template <typename T>

View File

@ -27,7 +27,7 @@ static inline QPromise<void> qPromise()
}
template <typename T, template <typename, typename...> class Sequence = QVector, typename ...Args>
static inline QPromise<QVector<T> > qPromiseAll(const Sequence<QPromise<T>, Args...>& promises)
static inline QPromise<QVector<T>> qPromiseAll(const Sequence<QPromise<T>, Args...>& promises)
{
return QPromise<T>::all(promises);
}

View File

@ -56,7 +56,7 @@ void tst_future::fulfilled()
return 42;
}));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int>>::value));
QCOMPARE(p.isPending(), true);
p.then([&](int res) {
@ -72,7 +72,7 @@ void tst_future::fulfilled_void()
int result = -1;
auto p = qPromise(QtConcurrent::run([]() { }));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
QCOMPARE(p.isPending(), true);
p.then([&]() {
@ -91,7 +91,7 @@ void tst_future::rejected()
return 42;
}));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int>>::value));
QCOMPARE(p.isPending(), true);
p.fail([&](const MyException& e) {
@ -110,7 +110,7 @@ void tst_future::rejected_void()
throw MyException("foo");
}));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
QCOMPARE(p.isPending(), true);
@ -130,7 +130,7 @@ void tst_future::unhandled()
return 42;
}));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int>>::value));
QCOMPARE(p.isPending(), true);
@ -153,7 +153,7 @@ void tst_future::unhandled_void()
throw QString("foo");
}));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
QCOMPARE(p.isPending(), true);
p.fail([&](const QString& err) {
@ -307,7 +307,7 @@ void tst_future::finally()
});
});
Q_STATIC_ASSERT((std::is_same<decltype(output), QPromise<int> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(output), QPromise<int>>::value));
QCOMPARE(input.isFulfilled(), true);
QCOMPARE(output.isPending(), true);
@ -330,7 +330,7 @@ void tst_future::finallyRejected()
});
});
Q_STATIC_ASSERT((std::is_same<decltype(output), QPromise<int> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(output), QPromise<int>>::value));
QCOMPARE(input.isFulfilled(), true);
QCOMPARE(output.isPending(), true);

View File

@ -33,7 +33,7 @@ void tst_helpers::resolve()
int value = -1;
auto p = QtPromise::qPromise(42);
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int>>::value));
QCOMPARE(p.isFulfilled(), true);
@ -49,7 +49,7 @@ void tst_helpers::resolve_void()
int value = -1;
auto p = QtPromise::qPromise();
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
QCOMPARE(p.isFulfilled(), true);
@ -70,7 +70,7 @@ void tst_helpers::resolve_promise()
});
}));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QString> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QString>>::value));
QCOMPARE(p.isPending(), true);
@ -93,7 +93,7 @@ void tst_helpers::resolve_promise_void()
});
}));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
QCOMPARE(p.isPending(), true);
@ -115,9 +115,9 @@ void tst_helpers::allFulfilled()
});
});
auto p = qPromiseAll(QVector<QPromise<int> >{p0, p2, p1});
auto p = qPromiseAll(QVector<QPromise<int>>{p0, p2, p1});
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int> > >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
QCOMPARE(p.isPending(), true);
QCOMPARE(p0.isFulfilled(), true);
@ -144,9 +144,9 @@ void tst_helpers::allFulfilled_void()
});
});
auto p = qPromiseAll(QVector<QPromise<void> >{p0, p2, p1});
auto p = qPromiseAll(QVector<QPromise<void>>{p0, p2, p1});
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
QCOMPARE(p.isPending(), true);
QCOMPARE(p0.isFulfilled(), true);
@ -169,9 +169,9 @@ void tst_helpers::allRejected()
});
});
auto p = qPromiseAll(QVector<QPromise<int> >{p0, p2, p1});
auto p = qPromiseAll(QVector<QPromise<int>>{p0, p2, p1});
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int> > >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
QCOMPARE(p.isPending(), true);
QCOMPARE(p0.isFulfilled(), true);
@ -199,9 +199,9 @@ void tst_helpers::allRejected_void()
});
});
auto p = qPromiseAll(QVector<QPromise<void> >{p0, p2, p1});
auto p = qPromiseAll(QVector<QPromise<void>>{p0, p2, p1});
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
QCOMPARE(p.isPending(), true);
QCOMPARE(p0.isFulfilled(), true);
@ -220,9 +220,9 @@ void tst_helpers::allRejected_void()
void tst_helpers::allEmpty()
{
auto p = qPromiseAll(QVector<QPromise<int> >());
auto p = qPromiseAll(QVector<QPromise<int>>());
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int> > >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
QCOMPARE(p.isFulfilled(), true);
@ -236,9 +236,9 @@ void tst_helpers::allEmpty()
void tst_helpers::allEmpty_void()
{
auto p = qPromiseAll(QVector<QPromise<void> >());
auto p = qPromiseAll(QVector<QPromise<void>>());
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
QCOMPARE(p.isFulfilled(), true);
}

View File

@ -31,7 +31,7 @@ struct SequenceTester
};
template <template <typename, typename...> class Sequence, typename ...Args>
struct SequenceTester<Sequence<QPromise<int>, Args...> >
struct SequenceTester<Sequence<QPromise<int>, Args...>>
{
static void exec()
{
@ -47,13 +47,13 @@ struct SequenceTester<Sequence<QPromise<int>, Args...> >
auto p = QPromise<int>::all(promises);
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int> > >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({42, 46, 43, 44}));
}
};
template <template <typename, typename...> class Sequence, typename ...Args>
struct SequenceTester<Sequence<QPromise<void>, Args...> >
struct SequenceTester<Sequence<QPromise<void>, Args...>>
{
static void exec()
{
@ -69,7 +69,7 @@ struct SequenceTester<Sequence<QPromise<void>, Args...> >
auto p = QPromise<void>::all(promises);
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
QCOMPARE(waitForValue(p, -1, 42), 42);
}
};
@ -78,26 +78,26 @@ struct SequenceTester<Sequence<QPromise<void>, Args...> >
void tst_qpromise_all::qList()
{
SequenceTester<QList<QPromise<int> > >::exec();
SequenceTester<QList<QPromise<void> > >::exec();
SequenceTester<QList<QPromise<int>>>::exec();
SequenceTester<QList<QPromise<void>>>::exec();
}
// QVector::push_back/append isn't supported since it requires a default
// constructor (see https://github.com/simonbrunel/qtpromise/issues/3)
//void tst_qpromise_all::qVector()
//{
// SequenceTester<QVector<QPromise<int> > >::exec();
// SequenceTester<QVector<QPromise<void> > >::exec();
// SequenceTester<QVector<QPromise<int>>>::exec();
// SequenceTester<QVector<QPromise<void>>>::exec();
//}
void tst_qpromise_all::stdList()
{
SequenceTester<std::list<QPromise<int> > >::exec();
SequenceTester<std::list<QPromise<void> > >::exec();
SequenceTester<std::list<QPromise<int>>>::exec();
SequenceTester<std::list<QPromise<void>>>::exec();
}
void tst_qpromise_all::stdVector()
{
SequenceTester<std::vector<QPromise<int> > >::exec();
SequenceTester<std::vector<QPromise<void> > >::exec();
SequenceTester<std::vector<QPromise<int>>>::exec();
SequenceTester<std::vector<QPromise<void>>>::exec();
}

View File

@ -0,0 +1,4 @@
TARGET = tst_qpromise_cast
SOURCES += $$PWD/tst_cast.cpp
include(../../qtpromise.pri)

View File

@ -0,0 +1,135 @@
// Tests
#include "../../shared/utils.h"
// QtPromise
#include <QtPromise>
// Qt
#include <QtTest>
using namespace QtPromise;
class tst_qpromise_cast : public QObject
{
Q_OBJECT
private Q_SLOTS:
void typeToVoid();
//void castToVoidOperator();
//void typeToVariant(); // QPromise<T>.cast<QVariant>()
//void variantToType();
//void voidToVariant(); // QPromise<void>.cast<QVariant>()
//void variantToVoid();
//void jsonValueToJsonObject();
};
QTEST_MAIN(tst_qpromise_cast)
#include "tst_cast.moc"
void tst_qpromise_cast::typeToVoid()
{
QPromise<void> p0 = QPromise<int>::resolve(42);
QPromise<void> p1 = QPromise<QString>::resolve(QString("foo"));
QVERIFY(p0.isFulfilled());
QVERIFY(p1.isFulfilled());
}
//void tst_qpromise_cast::castToVoidOperator()
//{
//auto p0 = QPromise<int>::resolve(42);
//QPromise<double> p1(p0);
//QPromise<void> p2(p0);
//auto p4 = QPromise<QString>::resolve("foo");
//
//p0.then([](int res) { qDebug() << res; });
//p1.then([](double res) { qDebug() << res; });
//p2.then([]() { qDebug() << "done"; });
//
//foo().then([]() {
// qDebug() << "done";
//}).wait();
//
//QPromise<QVariant>::all({p0, p1, p4}).then([](const QVector<QVariant>& res) {
// qDebug() << "all done!" << res;
//}).wait();
//}
/*
namespace {
template <typename T, typename U>
void test_qpromise_cast(const T& t, const U& u)
{
auto p = QPromise<T>::resolve(t).cast<U>();
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<U> >::value));
QCOMPARE(waitForValue(p, U()), u);
}
template <typename U>
void test_qpromise_cast(const U& u)
{
auto p = QPromise<void>::resolve().cast<U>();
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<U> >::value));
QCOMPARE(waitForValue(p, U()), u);
}
} // anonymous namespace
void tst_qpromise_cast::typeToVariant()
{
test_qpromise_cast(42, QVariant(42));
test_qpromise_cast(4.2, QVariant(4.2));
test_qpromise_cast(true, QVariant(true));
test_qpromise_cast(QString("foo"), QVariant(QString("foo")));
test_qpromise_cast(QUrl("http://x.y.z"), QVariant(QUrl("http://x.y.z")));
test_qpromise_cast(QSize(128, 256), QVariant(QSize(128, 256)));
test_qpromise_cast(QDate(2018, 1, 1), QVariant(QDate(2018, 1, 1)));
test_qpromise_cast(QJsonValue("foo"), QVariant(QJsonValue("foo")));
test_qpromise_cast(QStringList{"foo", "bar"}, QVariant(QStringList{"foo", "bar"}));
test_qpromise_cast(QList<QVariant>{"foo", 42}, QVariant(QList<QVariant>{"foo", 42}));
test_qpromise_cast(QMap<QString, QVariant>{{"foo", 42}}, QVariant(QVariantMap{{"foo", 42}}));
}
void tst_qpromise_cast::voidToVariant()
{
test_qpromise_cast(QVariant());
}
void tst_qpromise_cast::variantToType()
{
// invalid
// int
// QString
// QColor
// QList
}
/*
void tst_qpromise_cast::jsonValueToJsonObject()
{
{ // QJsonValue(Null) -> QJsonObject
auto p = QPromise<QJsonValue>::resolve(QJsonValue()).cast<QJsonObject>();
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QJsonObject> >::value));
const QJsonObject res = waitForValue(p, QJsonObject());
QVERIFY(res.isEmpty());
}
{ // QJsonValue(int) -> QJsonObject
auto p = QPromise<QJsonValue>::resolve(QJsonValue(42)).cast<QJsonObject>();
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QJsonObject> >::value));
const QJsonObject res = waitForValue(p, QJsonObject());
QVERIFY(res.isEmpty());
}
{ // QJsonValue(QJsonObject) -> QJsonObject
const QJsonObject object{{"magic", 42}};
auto p = QPromise<QJsonValue>::resolve(QJsonValue(object)).cast<QJsonObject>();
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QJsonObject> >::value));
const QJsonObject res = waitForValue(p, QJsonObject());
QCOMPARE(res.value("magic"), 42);
}
}
*/

View File

@ -7,6 +7,9 @@
// Qt
#include <QtTest>
// STL
#include <memory>
using namespace QtPromise;
class tst_qpromise_construct : public QObject
@ -30,6 +33,8 @@ private Q_SLOTS:
void rejectSync_void();
void rejectAsync();
void rejectAsync_void();
void connectAndResolve();
void connectAndReject();
};
QTEST_MAIN(tst_qpromise_construct)
@ -228,3 +233,77 @@ void tst_qpromise_construct::rejectThrowTwoArgs_void()
QCOMPARE(waitForValue(p, -1, 42), -1);
QCOMPARE(waitForError(p, QString()), QString("foo"));
}
// https://github.com/simonbrunel/qtpromise/issues/6
void tst_qpromise_construct::connectAndResolve()
{
QScopedPointer<QObject> object(new QObject());
std::weak_ptr<int> wptr;
{
auto p = QPromise<std::shared_ptr<int>>([&](
const QPromiseResolve<std::shared_ptr<int>>& resolve,
const QPromiseReject<std::shared_ptr<int>>& reject) {
connect(object.data(), &QObject::objectNameChanged,
[=, &wptr](const QString& name) {
std::shared_ptr<int> sptr(new int(42));
wptr = sptr;
if (name == "foobar") {
resolve(sptr);
} else {
reject(42);
}
});
});
QCOMPARE(p.isPending(), true);
object->setObjectName("foobar");
QCOMPARE(waitForValue(p, std::shared_ptr<int>()), wptr.lock());
QCOMPARE(wptr.use_count(), 1l); // "p" still holds a reference
}
QCOMPARE(wptr.use_count(), 0l);
}
// https://github.com/simonbrunel/qtpromise/issues/6
void tst_qpromise_construct::connectAndReject()
{
QScopedPointer<QObject> object(new QObject());
std::weak_ptr<int> wptr;
{
auto p = QPromise<int>([&](
const QPromiseResolve<int>& resolve,
const QPromiseReject<int>& reject) {
connect(object.data(), &QObject::objectNameChanged,
[=, &wptr](const QString& name) {
std::shared_ptr<int> sptr(new int(42));
wptr = sptr;
if (name == "foobar") {
reject(sptr);
} else {
resolve(42);
}
});
});
QCOMPARE(p.isPending(), true);
object->setObjectName("foobar");
QCOMPARE(waitForError(p, std::shared_ptr<int>()), wptr.lock());
QCOMPARE(wptr.use_count(), 1l); // "p" still holds a reference
}
QCOMPARE(wptr.use_count(), 0l);
}

View File

@ -39,7 +39,7 @@ void tst_qpromise_finally::fulfilledSync()
return 16; // ignored!
});
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int>>::value));
QCOMPARE(waitForValue(p, -1), 42);
QCOMPARE(p.isFulfilled(), true);
QCOMPARE(value, 8);
@ -53,7 +53,7 @@ void tst_qpromise_finally::fulfilledSync_void()
return 16; // ignored!
});
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
QCOMPARE(waitForValue(p, -1, 42), 42);
QCOMPARE(p.isFulfilled(), true);
QCOMPARE(value, 8);
@ -65,7 +65,7 @@ void tst_qpromise_finally::fulfilledThrows()
throw QString("bar");
});
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int>>::value));
QCOMPARE(waitForError(p, QString()), QString("bar"));
QCOMPARE(p.isRejected(), true);
}
@ -76,7 +76,7 @@ void tst_qpromise_finally::fulfilledThrows_void()
throw QString("bar");
});
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
QCOMPARE(waitForError(p, QString()), QString("bar"));
QCOMPARE(p.isRejected(), true);
}
@ -123,7 +123,7 @@ void tst_qpromise_finally::rejectedSync()
return 16; // ignored!
});
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int>>::value));
QCOMPARE(waitForError(p, QString()), QString("foo"));
QCOMPARE(p.isRejected(), true);
QCOMPARE(value, 8);
@ -137,7 +137,7 @@ void tst_qpromise_finally::rejectedSync_void()
return 16; // ignored!
});
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
QCOMPARE(waitForError(p, QString()), QString("foo"));
QCOMPARE(p.isRejected(), true);
QCOMPARE(value, 8);
@ -149,7 +149,7 @@ void tst_qpromise_finally::rejectedThrows()
throw QString("bar");
});
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int>>::value));
QCOMPARE(waitForError(p, QString()), QString("bar"));
QCOMPARE(p.isRejected(), true);
}
@ -160,7 +160,7 @@ void tst_qpromise_finally::rejectedThrows_void()
throw QString("bar");
});
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
QCOMPARE(waitForError(p, QString()), QString("bar"));
QCOMPARE(p.isRejected(), true);
}

View File

@ -1,12 +1,15 @@
TEMPLATE = subdirs
SUBDIRS += \
all \
cast \
construct \
delay \
fail \
finally \
operators \
reject \
resolve \
tap \
tapfail \
then \
timeout

View File

@ -0,0 +1,4 @@
TARGET = tst_qpromise_reject
SOURCES += $$PWD/tst_reject.cpp
include(../../qtpromise.pri)

View File

@ -0,0 +1,74 @@
// Tests
#include "../../shared/utils.h"
// QtPromise
#include <QtPromise>
// Qt
#include <QtTest>
// STL
#include <memory>
using namespace QtPromise;
class tst_qpromise_reject : public QObject
{
Q_OBJECT
private Q_SLOTS:
void rejectWithValue();
void rejectWithQSharedPtr();
void rejectWithStdSharedPtr();
};
QTEST_MAIN(tst_qpromise_reject)
#include "tst_reject.moc"
void tst_qpromise_reject::rejectWithValue()
{
auto p = QPromise<int>::reject(42);
QCOMPARE(p.isRejected(), true);
QCOMPARE(waitForError(p, -1), 42);
}
// https://github.com/simonbrunel/qtpromise/issues/6
void tst_qpromise_reject::rejectWithQSharedPtr()
{
QWeakPointer<int> wptr;
{
QSharedPointer<int> sptr(new int(42));
auto p = QPromise<int>::reject(sptr);
QCOMPARE(waitForError(p, QSharedPointer<int>()), sptr);
wptr = sptr;
sptr.reset();
QCOMPARE(wptr.isNull(), false); // "p" still holds a reference
}
QCOMPARE(wptr.isNull(), true);
}
// https://github.com/simonbrunel/qtpromise/issues/6
void tst_qpromise_reject::rejectWithStdSharedPtr()
{
std::weak_ptr<int> wptr;
{
std::shared_ptr<int> sptr(new int(42));
auto p = QPromise<int>::reject(sptr);
QCOMPARE(waitForError(p, std::shared_ptr<int>()), sptr);
wptr = sptr;
sptr.reset();
QCOMPARE(wptr.use_count(), 1l); // "p" still holds a reference
}
QCOMPARE(wptr.use_count(), 0l);
}

View File

@ -7,6 +7,9 @@
// Qt
#include <QtTest>
// STL
#include <memory>
using namespace QtPromise;
class tst_qpromise_resolve : public QObject
@ -14,14 +17,16 @@ class tst_qpromise_resolve : public QObject
Q_OBJECT
private Q_SLOTS:
void value();
void empty_void();
void resolveWithValue();
void resolveWithNoValue();
void resolveWithQSharedPtr();
void resolveWithStdSharedPtr();
};
QTEST_MAIN(tst_qpromise_resolve)
#include "tst_resolve.moc"
void tst_qpromise_resolve::value()
void tst_qpromise_resolve::resolveWithValue()
{
const int value = 42;
auto p0 = QPromise<int>::resolve(value);
@ -33,9 +38,49 @@ void tst_qpromise_resolve::value()
QCOMPARE(waitForValue(p1, -1), 43);
}
void tst_qpromise_resolve::empty_void()
void tst_qpromise_resolve::resolveWithNoValue()
{
auto p = QPromise<void>::resolve();
QCOMPARE(p.isFulfilled(), true);
}
// https://github.com/simonbrunel/qtpromise/issues/6
void tst_qpromise_resolve::resolveWithQSharedPtr()
{
QWeakPointer<int> wptr;
{
QSharedPointer<int> sptr(new int(42));
auto p = QPromise<QSharedPointer<int>>::resolve(sptr);
QCOMPARE(waitForValue(p, QSharedPointer<int>()), sptr);
wptr = sptr;
sptr.reset();
QCOMPARE(wptr.isNull(), false); // "p" still holds a reference
}
QCOMPARE(wptr.isNull(), true);
}
// https://github.com/simonbrunel/qtpromise/issues/6
void tst_qpromise_resolve::resolveWithStdSharedPtr()
{
std::weak_ptr<int> wptr;
{
std::shared_ptr<int> sptr(new int(42));
auto p = QPromise<std::shared_ptr<int>>::resolve(sptr);
QCOMPARE(waitForValue(p, std::shared_ptr<int>()), sptr);
wptr = sptr;
sptr.reset();
QCOMPARE(wptr.use_count(), 1l); // "p" still holds a reference
}
QCOMPARE(wptr.use_count(), 0l);
}

View File

@ -0,0 +1,4 @@
TARGET = tst_qpromise_tapfail
SOURCES += $$PWD/tst_tapfail.cpp
include(../../qtpromise.pri)

View File

@ -0,0 +1,159 @@
// Tests
#include "../../shared/utils.h"
// QtPromise
#include <QtPromise>
// Qt
#include <QtTest>
using namespace QtPromise;
class tst_qpromise_tapfail : public QObject
{
Q_OBJECT
private Q_SLOTS:
void fulfilled();
void fulfilled_void();
void rejected();
void rejected_void();
void throws();
void throws_void();
void delayedResolved();
void delayedRejected();
};
QTEST_MAIN(tst_qpromise_tapfail)
#include "tst_tapfail.moc"
void tst_qpromise_tapfail::fulfilled()
{
int value = -1;
auto p = QPromise<int>::resolve(42).tapFail([&]() {
value = 43;
});
QCOMPARE(waitForValue(p, 42), 42);
QCOMPARE(p.isFulfilled(), true);
QCOMPARE(value, -1);
}
void tst_qpromise_tapfail::fulfilled_void()
{
int value = -1;
auto p = QPromise<void>::resolve().tapFail([&]() {
value = 43;
});
QCOMPARE(waitForValue(p, -1, 42), 42);
QCOMPARE(p.isFulfilled(), true);
QCOMPARE(value, -1);
}
void tst_qpromise_tapfail::rejected()
{
QStringList errors;
auto p0 = QPromise<int>::reject(QString("foo"))
.tapFail([&](const QString& err) {
errors << "1:" + err;
});
auto p1 = p0
.fail([&](const QString& err) {
errors << "2:" + err;
return 43;
});
QCOMPARE(waitForError(p0, QString()), QString("foo"));
QCOMPARE(waitForValue(p1, -1), 43);
QCOMPARE(p0.isRejected(), true);
QCOMPARE(p1.isFulfilled(), true);
QCOMPARE(errors, QStringList() << "1:foo" << "2:foo");
}
void tst_qpromise_tapfail::rejected_void()
{
QStringList errors;
auto p0 = QPromise<void>::reject(QString("foo"))
.tapFail([&](const QString& err) {
errors << "1:" + err;
});
auto p1 = p0
.fail([&](const QString& err) {
errors << "2:" + err;
});
QCOMPARE(waitForError(p0, QString()), QString("foo"));
QCOMPARE(waitForValue(p1, -1, 43), 43);
QCOMPARE(p0.isRejected(), true);
QCOMPARE(p1.isFulfilled(), true);
QCOMPARE(errors, QStringList() << "1:foo" << "2:foo");
}
void tst_qpromise_tapfail::throws()
{
auto p = QPromise<int>::reject(QString("foo"))
.tapFail([&]() {
throw QString("bar");
});
QCOMPARE(waitForError(p, QString()), QString("bar"));
QCOMPARE(p.isRejected(), true);
}
void tst_qpromise_tapfail::throws_void()
{
auto p = QPromise<void>::reject(QString("foo"))
.tapFail([&]() {
throw QString("bar");
});
QCOMPARE(waitForError(p, QString()), QString("bar"));
QCOMPARE(p.isRejected(), true);
}
void tst_qpromise_tapfail::delayedResolved()
{
QVector<int> values;
auto p = QPromise<int>::reject(QString("foo"))
.tapFail([&]() {
QPromise<void> p([&](const QPromiseResolve<void>& resolve) {
QtPromisePrivate::qtpromise_defer([=, &values]() {
values << 3;
resolve(); // ignored!
});
});
values << 2;
return p;
});
QCOMPARE(waitForError(p, QString()), QString("foo"));
QCOMPARE(values, QVector<int>({2, 3}));
}
void tst_qpromise_tapfail::delayedRejected()
{
QVector<int> values;
auto p = QPromise<int>::reject(QString("foo"))
.tapFail([&]() {
QPromise<void> p([&](
const QPromiseResolve<void>&,
const QPromiseReject<void>& reject){
QtPromisePrivate::qtpromise_defer([=, &values]() {
values << 3;
reject(QString("bar"));
});
});
values << 2;
return p;
});
QCOMPARE(waitForError(p, QString()), QString("bar"));
QCOMPARE(values, QVector<int>({2, 3}));
}

View File

@ -56,7 +56,7 @@ void tst_qpromise_then::resolveAsync()
});
});
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QString> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QString>>::value));
QCOMPARE(waitForValue(p, QString()), QString("foo42"));
QCOMPARE(p.isFulfilled(), true);
}
@ -91,7 +91,7 @@ void tst_qpromise_then::rejectAsync()
});
});
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
QCOMPARE(waitForError(p, QString()), QString("foo42"));
QCOMPARE(p.isRejected(), true);
}
@ -105,7 +105,7 @@ void tst_qpromise_then::skipResult()
value = 43;
}).wait();
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int>>::value));
QCOMPARE(value, 43);
}

View File

@ -1,10 +1,15 @@
TEMPLATE = app
CONFIG += testcase
CONFIG += testcase warn_on
QT += testlib
QT -= gui
DEFINES += QT_DEPRECATED_WARNINGS
# Additional warnings and make all warnings into errors
# https://github.com/simonbrunel/qtpromise/issues/10
gcc:QMAKE_CXXFLAGS += -Werror -Wold-style-cast
msvc:QMAKE_CXXFLAGS -= -WX
coverage {
gcc {
message("Code coverage enabled (gcov)")

View File

@ -279,7 +279,7 @@ void tst_requirements::thenMultipleCalls()
});
});
qPromiseAll(QVector<QPromise<void> >{
qPromiseAll(QVector<QPromise<void>>{
p.then([&](int r) { values << r + 1; }),
p.then([&](int r) { values << r + 2; }),
p.then([&](int r) { values << r + 3; })
@ -298,7 +298,7 @@ void tst_requirements::thenMultipleCalls()
});
});
qPromiseAll(QVector<QPromise<int> >{
qPromiseAll(QVector<QPromise<int>>{
p.then(nullptr, [&](int r) { values << r + 1; return r + 1; }),
p.then(nullptr, [&](int r) { values << r + 2; return r + 2; }),
p.then(nullptr, [&](int r) { values << r + 3; return r + 3; })
@ -314,9 +314,9 @@ void tst_requirements::thenHandlers()
{
auto handler = [](){ return 42; };
auto p1 = QPromise<int>::resolve(42);
Q_STATIC_ASSERT((std::is_same<decltype(p1.then(handler, nullptr)), QPromise<int> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1.then(nullptr, handler)), QPromise<int> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1.then(handler, handler)), QPromise<int> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1.then(handler, nullptr)), QPromise<int>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1.then(nullptr, handler)), QPromise<int>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1.then(handler, handler)), QPromise<int>>::value));
}
// 2.2.7.1. If either onFulfilled or onRejected returns a value x, run the
@ -351,7 +351,7 @@ void tst_requirements::thenHandlers()
QString value;
auto p1 = QPromise<QString>::resolve("42");
auto p2 = p1.then(nullptr, [](){ return QString(); });
Q_STATIC_ASSERT((std::is_same<decltype(p2), QPromise<QString> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p2), QPromise<QString>>::value));
p2.then([&](const QString& e) { value = e; }).wait();
QVERIFY(p1.isFulfilled());

View File

@ -28,159 +28,159 @@ QTEST_MAIN(tst_thread)
void tst_thread::resolve()
{
int value = -1;
size_t target = 0;
size_t source = 0;
QThread* target = nullptr;
QThread* source = nullptr;
QPromise<int>([&](const QPromiseResolve<int>& resolve) {
QtConcurrent::run([=, &source]() {
source = (size_t)QThread::currentThread();
source = QThread::currentThread();
resolve(42);
});
}).then([&](int res) {
target = (size_t)QThread::currentThread();
target = QThread::currentThread();
value = res;
}).wait();
QVERIFY(source != 0);
QVERIFY(source != nullptr);
QVERIFY(source != target);
QCOMPARE(target, (size_t)QThread::currentThread());
QCOMPARE(target, QThread::currentThread());
QCOMPARE(value, 42);
}
void tst_thread::resolve_void()
{
int value = -1;
size_t target = 0;
size_t source = 0;
QThread* target = nullptr;
QThread* source = nullptr;
QPromise<void>([&](const QPromiseResolve<void>& resolve) {
QtConcurrent::run([=, &source]() {
source = (size_t)QThread::currentThread();
source = QThread::currentThread();
resolve();
});
}).then([&]() {
target = (size_t)QThread::currentThread();
target = QThread::currentThread();
value = 43;
}).wait();
QVERIFY(source != 0);
QVERIFY(source != nullptr);
QVERIFY(source != target);
QCOMPARE(target, (size_t)QThread::currentThread());
QCOMPARE(target, QThread::currentThread());
QCOMPARE(value, 43);
}
void tst_thread::reject()
{
QString error;
size_t target = 0;
size_t source = 0;
QThread* target = nullptr;
QThread* source = nullptr;
QPromise<int>([&](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
QtConcurrent::run([=, &source]() {
source = (size_t)QThread::currentThread();
source = QThread::currentThread();
reject(QString("foo"));
});
}).fail([&](const QString& err) {
target = (size_t)QThread::currentThread();
target = QThread::currentThread();
error = err;
return -1;
}).wait();
QVERIFY(source != 0);
QVERIFY(source != nullptr);
QVERIFY(source != target);
QCOMPARE(target, (size_t)QThread::currentThread());
QCOMPARE(target, QThread::currentThread());
QCOMPARE(error, QString("foo"));
}
void tst_thread::then()
{
size_t source;
QThread* source = nullptr;
QPromise<int> p([&](const QPromiseResolve<int>& resolve) {
source = (size_t)QThread::currentThread();
source = QThread::currentThread();
resolve(42);
});
size_t target;
int value = -1;
QThread* target = nullptr;
qPromise(QtConcurrent::run([&](const QPromise<int>& p) {
p.then([&](int res) {
target = (size_t)QThread::currentThread();
target = QThread::currentThread();
value = res;
}).wait();
}, p)).wait();
QVERIFY(target != 0);
QVERIFY(target != nullptr);
QVERIFY(source != target);
QCOMPARE(source, (size_t)QThread::currentThread());
QCOMPARE(source, QThread::currentThread());
QCOMPARE(value, 42);
}
void tst_thread::then_void()
{
size_t source;
QThread* source = nullptr;
QPromise<void> p([&](const QPromiseResolve<void>& resolve) {
source = (size_t)QThread::currentThread();
source = QThread::currentThread();
resolve();
});
size_t target;
int value = -1;
QThread* target = nullptr;
qPromise(QtConcurrent::run([&](const QPromise<void>& p) {
p.then([&]() {
target = (size_t)QThread::currentThread();
target = QThread::currentThread();
value = 43;
}).wait();
}, p)).wait();
QVERIFY(target != 0);
QVERIFY(target != nullptr);
QVERIFY(source != target);
QCOMPARE(source, (size_t)QThread::currentThread());
QCOMPARE(source, QThread::currentThread());
QCOMPARE(value, 43);
}
void tst_thread::fail()
{
size_t source;
QThread* source = nullptr;
QPromise<int> p([&](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
source = (size_t)QThread::currentThread();
source = QThread::currentThread();
reject(QString("foo"));
});
size_t target;
QString error;
QThread* target = nullptr;
qPromise(QtConcurrent::run([&](const QPromise<int>& p) {
p.fail([&](const QString& err) {
target = (size_t)QThread::currentThread();
target = QThread::currentThread();
error = err;
return -1;
}).wait();
}, p)).wait();
QVERIFY(target != 0);
QVERIFY(target != nullptr);
QVERIFY(source != target);
QCOMPARE(source, (size_t)QThread::currentThread());
QCOMPARE(source, QThread::currentThread());
QCOMPARE(error, QString("foo"));
}
void tst_thread::finally()
{
size_t source;
QThread* source = nullptr;
QPromise<int> p([&](const QPromiseResolve<int>& resolve) {
source = (size_t)QThread::currentThread();
source = QThread::currentThread();
resolve(42);
});
size_t target;
int value = -1;
QThread* target = nullptr;
qPromise(QtConcurrent::run([&](const QPromise<int>& p) {
p.finally([&]() {
target = (size_t)QThread::currentThread();
target = QThread::currentThread();
value = 43;
}).wait();
}, p)).wait();
QVERIFY(target != 0);
QVERIFY(target != nullptr);
QVERIFY(source != target);
QCOMPARE(source, (size_t)QThread::currentThread());
QCOMPARE(source, QThread::currentThread());
QCOMPARE(value, 43);
}