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
56 changed files with 937 additions and 1230 deletions

1
.gitignore vendored
View File

@ -8,7 +8,6 @@ node_modules
*.obj
*.exe
*.user
*.qmlc
Makefile*
moc_*.cpp
moc_*.h

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

@ -1,6 +0,0 @@
#ifndef QTQMLPROMISE_MODULE_H
#define QTQMLPROMISE_MODULE_H
#include "../src/qtqmlpromise/qjspromise.h"
#endif // ifndef QTQMLPROMISE_MODULE_H

View File

@ -1,11 +1,3 @@
INCLUDEPATH += $$PWD/include $$PWD/src
DEPENDPATH += $$PWD/include $$PWD/src
CONFIG += c++11
qtpromise-qml {
QML_IMPORT_PATH += $$shadowed($$PWD)/qml
# To avoid carrying an extra library dependency, the QJSPromise definition is
# embedded in the QML plugin, so we need to link against the plugin itself.
LIBS += -L$$shadowed($$PWD)/qml/QtPromise -l$$qtLibraryTarget(qtpromiseplugin)
}

View File

@ -1,9 +1,10 @@
TEMPLATE = subdirs
SUBDIRS = \
src \
tests
tests.depends = src
_qt_creator_ {
SUBDIRS += src
}
OTHER_FILES = \
package/features/*.prf \

View File

@ -1,25 +0,0 @@
TEMPLATE = lib
CONFIG += plugin exceptions
QT += qml
IMPORT_VERSION = 1.0
DEFINES += QTQMLPROMISE_LIBRARY
TARGET = $$qtLibraryTarget(qtpromiseplugin)
DESTDIR = $$shadowed($$PWD/../../qml/QtPromise)
include(../qtqmlpromise/qtqmlpromise.pri)
include(../../qtpromise.pri)
SOURCES += \
$$PWD/plugin.cpp
QMLFILES += \
$$PWD/plugins.qmltypes \
$$PWD/qmldir
RESOURCES += $$PWD/imports.qrc
qmlfiles.files = $$QMLFILES
qmlfiles.path = $$DESTDIR
COPIES += qmlfiles

View File

@ -1,5 +0,0 @@
<RCC>
<qresource prefix="/QtPromise">
<file>qtqmlpromise.js</file>
</qresource>
</RCC>

View File

@ -1,76 +0,0 @@
// QtPromise
#include <QtQmlPromise>
// Qt
#include <QQmlEngine>
#include <QQmlExtensionPlugin>
static const char* kJSPromisePrivateNamespace = "__qtpromise_private__";
using namespace QtPromise;
class QtQmlPromiseObject : public QObject
{
Q_OBJECT
public:
QtQmlPromiseObject(QJSEngine* engine)
: QObject(engine)
, m_engine(engine)
{
Q_ASSERT(engine);
}
Q_INVOKABLE QJSValue create(const QJSValue& resolver, const QJSValue& prototype)
{
QJSValue value = m_engine->toScriptValue(QJSPromise(m_engine, resolver));
value.setPrototype(prototype);
return value;
}
Q_INVOKABLE QtPromise::QJSPromise resolve(QJSValue value)
{
return QJSPromise::resolve(std::move(value));
}
Q_INVOKABLE QtPromise::QJSPromise reject(QJSValue error)
{
return QJSPromise::reject(std::move(error));
}
Q_INVOKABLE QtPromise::QJSPromise all(QJSValue input)
{
return QJSPromise::all(m_engine, std::move(input));
}
private:
QJSEngine* m_engine;
};
class QtQmlPromisePlugin : public QQmlExtensionPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
public:
void registerTypes(const char* uri) Q_DECL_OVERRIDE
{
Q_ASSERT(QLatin1String(uri) == QLatin1String("QtPromise"));
Q_UNUSED(uri);
qRegisterMetaType<QJSPromise>();
}
void initializeEngine(QQmlEngine* engine, const char* uri) Q_DECL_OVERRIDE
{
Q_ASSERT(QLatin1String(uri) == QLatin1String("QtPromise"));
Q_UNUSED(uri);
QJSValue global = engine->globalObject();
QJSValue object = engine->newQObject(new QtQmlPromiseObject(engine));
global.setProperty(kJSPromisePrivateNamespace, object);
}
}; // class QtQmlPromisePlugin
#include "plugin.moc"

View File

@ -1,5 +0,0 @@
import QtQuick.tooling 1.2
Module {
dependencies: []
}

View File

@ -1,5 +0,0 @@
module QtPromise
plugin qtpromiseplugin
typeinfo plugins.qmltypes
classname QtQmlPromisePlugin
Promise 1.0 qrc:///QtPromise/qtqmlpromise.js

View File

@ -1,16 +0,0 @@
.pragma library
(function(global) {
var private = global.__qtpromise_private__;
delete global.__qtpromise_private__;
var Promise = global.Promise = function(resolver) {
return private.create(function(proxy) {
resolver(proxy.resolve, proxy.reject);
}, this);
};
['all', 'reject', 'resolve'].forEach(function(method) {
Promise[method] = private[method];
});
})(this);

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

@ -1,5 +1,5 @@
TEMPLATE = aux
CONFIG += c++11 force_qt thread warn_on
TEMPLATE = lib
CONFIG += c++11 qt thread warn_on
DEFINES += QT_DEPRECATED_WARNINGS
include(qtpromise.pri)

View File

@ -1,175 +0,0 @@
// QtQmlPromise
#include "qjspromise.h"
#include "qjspromise_p.h"
// Qt
#include <QJSValueIterator>
#include <QJSEngine>
using namespace QtPromise;
namespace {
QJSValue toJSArray(QJSEngine* engine, const QVector<QJSValue>& values)
{
Q_ASSERT(engine);
const int count = values.count();
QJSValue array = engine->newArray(count);
for (int i = 0; i < count; ++i) {
array.setProperty(i, values[i]);
}
return array;
}
template <typename T>
QJSValue newError(const QJSValue& source, const T& value)
{
QJSEngine* engine = source.engine();
QJSValue error = engine->evaluate("throw new Error('')");
error.setProperty("message", engine->toScriptValue(value));
return error;
}
} // anonymous namespace
QJSPromise::QJSPromise()
: m_promise(QPromise<QJSValue>::resolve(QJSValue()))
{ }
QJSPromise::QJSPromise(QPromise<QJSValue>&& promise)
: m_promise(std::move(promise))
{
}
QJSPromise::QJSPromise(QJSEngine* engine, QJSValue resolver)
: m_promise([=](
const QPromiseResolve<QJSValue>& resolve,
const QPromiseReject<QJSValue>& reject) mutable {
// resolver is part of the Promise wrapper in qtqmlpromise.js
Q_ASSERT(resolver.isCallable());
Q_ASSERT(engine);
auto proxy = QtPromisePrivate::JSPromiseResolver(resolve, reject);
auto ret = resolver.call(QJSValueList() << engine->toScriptValue(proxy));
if (ret.isError()) {
throw ret;
}
})
{ }
QJSPromise QJSPromise::then(QJSValue fulfilled, QJSValue rejected) const
{
const bool fulfillable = fulfilled.isCallable();
const bool rejectable = rejected.isCallable();
if (!fulfillable && !rejectable) {
return *this;
}
auto _rejected = [=]() mutable {
QJSValue error;
try {
throw;
} catch (const QJSValue& err) {
error = err;
} catch (const QVariant& err) {
error = newError(rejected, err);
} catch (const std::exception& e) {
error = newError(rejected, QString(e.what()));
} catch (...) {
error = newError(rejected, QString("Unknown error"));
}
return rejected.call({error});
};
if (!fulfillable) {
return m_promise.then(nullptr, _rejected);
}
auto _fulfilled = [=](const QJSValue& res) mutable {
return fulfilled.call(QJSValueList() << res);
};
if (!rejectable) {
return m_promise.then(_fulfilled);
}
return m_promise.then(_fulfilled, _rejected);
}
QJSPromise QJSPromise::fail(QJSValue handler) const
{
return then(QJSValue(), handler);
}
QJSPromise QJSPromise::finally(QJSValue handler) const
{
return m_promise.finally([=]() mutable {
return handler.call();
});
}
QJSPromise QJSPromise::tap(QJSValue handler) const
{
return m_promise.tap([=](const QJSValue& res) mutable {
return handler.call(QJSValueList() << res);
});
}
QJSPromise QJSPromise::delay(int msec) const
{
return m_promise.delay(msec);
}
QJSPromise QJSPromise::wait() const
{
return m_promise.wait();
}
QJSPromise QJSPromise::resolve(QJSValue&& value)
{
return QPromise<QJSValue>::resolve(std::forward<QJSValue>(value));
}
QJSPromise QJSPromise::reject(QJSValue&& error)
{
return QPromise<QJSValue>::reject(std::forward<QJSValue>(error));
}
QJSPromise QJSPromise::all(QJSEngine* engine, QJSValue&& input)
{
Q_ASSERT(engine);
if (!input.isArray()) {
// TODO TYPEERROR!
return QPromise<QJSValue>::reject("foobar");
}
Q_ASSERT(input.hasProperty("length"));
const int count = input.property("length").toInt();
if (!count) {
return QPromise<QJSValue>::resolve(QJSValue(input));
}
QList<QPromise<QJSValue> > promises;
for (int i = 0; i < count; ++i) {
QJSValue value = input.property(i);
const QVariant variant = value.toVariant();
if (variant.userType() == qMetaTypeId<QJSPromise>()) {
promises << variant.value<QJSPromise>().m_promise;
} else {
promises << QPromise<QJSValue>::resolve(std::move(value));
}
}
return QPromise<QJSValue>::all(promises)
.then([engine](const QVector<QJSValue>& results) {
return toJSArray(engine, results);
});
}

View File

@ -1,52 +0,0 @@
#ifndef QTQMLPROMISE_QJSPROMISE_H
#define QTQMLPROMISE_QJSPROMISE_H
// QtQmlPromise
#include "qtqmlpromiseglobal.h"
// QtPromise
#include <QtPromise>
// Qt
#include <QJSValue>
namespace QtPromise {
class QTQMLPROMISE_EXPORT QJSPromise
{
Q_GADGET
public:
QJSPromise();
QJSPromise(QJSEngine* engine, QJSValue resolver);
QJSPromise(QPromise<QJSValue>&& promise);
Q_INVOKABLE bool isFulfilled() const { return m_promise.isFulfilled(); }
Q_INVOKABLE bool isRejected() const { return m_promise.isRejected(); }
Q_INVOKABLE bool isPending() const{ return m_promise.isPending(); }
Q_INVOKABLE QtPromise::QJSPromise then(QJSValue fulfilled, QJSValue rejected = QJSValue()) const;
Q_INVOKABLE QtPromise::QJSPromise fail(QJSValue handler) const;
Q_INVOKABLE QtPromise::QJSPromise finally(QJSValue handler) const;
Q_INVOKABLE QtPromise::QJSPromise tap(QJSValue handler) const;
Q_INVOKABLE QtPromise::QJSPromise delay(int msec) const;
Q_INVOKABLE QtPromise::QJSPromise wait() const;
public: // STATICS
static QJSPromise resolve(QJSValue&& value);
static QJSPromise reject(QJSValue&& error);
static QJSPromise all(QJSEngine* engine, QJSValue&& input);
private:
friend struct QtPromisePrivate::PromiseFulfill<QJSValue>;
QPromise<QJSValue> m_promise;
}; // class QJSPromise
} // namespace QtPromise
Q_DECLARE_TYPEINFO(QtPromise::QJSPromise, Q_MOVABLE_TYPE);
Q_DECLARE_METATYPE(QtPromise::QJSPromise)
#endif // ifndef QTQMLPROMISE_QJSPROMISE_H

View File

@ -1,71 +0,0 @@
#ifndef QTQMLPROMISE_QJSPROMISE_P_H
#define QTQMLPROMISE_QJSPROMISE_P_H
// QtQmlPromise
#include "qjspromise.h"
// QtPromise
#include <QtPromise>
// Qt
#include <QJSValue>
namespace QtPromisePrivate {
template <>
struct PromiseFulfill<QJSValue>
{
static void call(
const QJSValue& value,
const QtPromise::QPromiseResolve<QJSValue>& resolve,
const QtPromise::QPromiseReject<QJSValue>& reject)
{
using namespace QtPromise;
if (value.isObject()) {
const QVariant variant = value.toVariant();
if (variant.userType() == qMetaTypeId<QJSPromise>()) {
const auto promise = variant.value<QJSPromise>().m_promise;
PromiseFulfill<QPromise<QJSValue> >::call(promise, resolve, reject);
return;
}
}
if (value.isError()) {
reject(value);
} else {
resolve(value);
}
}
};
class JSPromiseResolver
{
Q_GADGET
public:
JSPromiseResolver() {}
JSPromiseResolver(
const QtPromise::QPromiseResolve<QJSValue>& resolve,
const QtPromise::QPromiseReject<QJSValue>& reject)
: m_d(new Data{resolve, reject})
{ }
Q_INVOKABLE void resolve(QJSValue value = QJSValue()) { m_d->resolve(std::move(value)); }
Q_INVOKABLE void reject(QJSValue error = QJSValue()) { m_d->reject(std::move(error)); }
private:
struct Data
{
QtPromise::QPromiseResolve<QJSValue> resolve;
QtPromise::QPromiseReject<QJSValue> reject;
}; // struct Data
QSharedPointer<Data> m_d;
};
} // namespace QtPromisePrivate
Q_DECLARE_METATYPE(QtPromisePrivate::JSPromiseResolver)
#endif // ifndef QTQMLPROMISE_QJSPROMISE_P_H

View File

@ -1,7 +0,0 @@
HEADERS += \
$$PWD/qjspromise.h \
$$PWD/qjspromise_p.h \
$$PWD/qtqmlpromiseglobal.h
SOURCES += \
$$PWD/qjspromise.cpp

View File

@ -1,8 +0,0 @@
TEMPLATE = aux
CONFIG += c++11 force_qt thread warn_on
DEFINES += QT_DEPRECATED_WARNINGS
DEFINES += QTQMLPROMISE_LIBRARY
QT += qml
include($$PWD/../../qtpromise.pri)
include($$PWD/qtqmlpromise.pri)

View File

@ -1,16 +0,0 @@
#ifndef QTQMLPROMISE_QTQMLPROMISEGLOBAL_H
#define QTQMLPROMISE_QTQMLPROMISEGLOBAL_H
// QtPromise
#include <QtPromise>
// QtCore
#include <QtGlobal>
#ifdef QTQMLPROMISE_LIBRARY
# define QTQMLPROMISE_EXPORT Q_DECL_EXPORT
#else
# define QTQMLPROMISE_EXPORT Q_DECL_IMPORT
#endif
#endif // ifndef QTQMLPROMISE_QTQMLPROMISEGLOBAL_H

View File

@ -1,5 +1,2 @@
TEMPLATE = subdirs
SUBDIRS += \
imports \
qtpromise \
qtqmlpromise
SUBDIRS = qtpromise

View File

@ -1,4 +1,2 @@
TEMPLATE = subdirs
SUBDIRS += \
qtpromise \
qtqmlpromise
SUBDIRS += qtpromise

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);
}

View File

@ -1,5 +0,0 @@
TARGET = tst_qtqmlpromise_extension
SOURCES += $$PWD/tst_extension.cpp
OTHER_FILES += $$PWD/*.qml
include(../qtqmlpromise.pri)

View File

@ -1,10 +0,0 @@
#include <QtQuickTest/quicktest.h>
static void initialize()
{
qputenv("QML2_IMPORT_PATH", QTPROMISE_IMPORTPATH);
}
Q_COREAPP_STARTUP_FUNCTION(initialize)
QUICK_TEST_MAIN(extension)

View File

@ -1,39 +0,0 @@
import QtQuick 2.3
import QtPromise 1.0
import QtTest 1.0
TestCase {
name: "Extension"
function test_global() {
compare(typeof __qtpromise_private__, 'undefined');
compare(typeof Promise, 'function');
compare(typeof Promise.resolve, 'function');
compare(typeof Promise.reject, 'function');
}
function test_instance() {
var p = new Promise(function() {});
compare(Object.prototype.toString(p), '[object Object]');
compare(p instanceof Promise, true);
compare(typeof p, 'object');
}
function test_prototype() {
var p = new Promise(function() {});
[
'delay',
'fail',
'finally',
'isFulfilled',
'isRejected',
'isPending',
'tap',
'then',
'wait',
].forEach(function(name) {
compare(typeof p[name], 'function');
});
}
}

View File

@ -1,9 +0,0 @@
TEMPLATE = app
CONFIG += qmltestcase
CONFIG += qtpromise-qml
DEFINES += QT_DEPRECATED_WARNINGS
QTPROMISE_IMPORTPATH = $$shadowed($$PWD/../../../qml)
DEFINES += QTPROMISE_IMPORTPATH=\"\\\"$$QTPROMISE_IMPORTPATH\\\"\"
include(../../../qtpromise.pri)

View File

@ -1,4 +0,0 @@
TEMPLATE = subdirs
SUBDIRS += \
extension \
requirements

View File

@ -1,5 +0,0 @@
TARGET = tst_qtqmlpromise_requirements
SOURCES += $$PWD/tst_requirements.cpp
OTHER_FILES += $$PWD/*.qml
include(../qtqmlpromise.pri)

View File

@ -1,10 +0,0 @@
#include <QtQuickTest/quicktest.h>
static void initialize()
{
qputenv("QML2_IMPORT_PATH", QTPROMISE_IMPORTPATH);
}
Q_COREAPP_STARTUP_FUNCTION(initialize)
QUICK_TEST_MAIN(requirements)

View File

@ -1,384 +0,0 @@
import QtQuick 2.3
import QtPromise 1.0
import QtTest 1.0
// https://promisesaplus.com/#requirements
TestCase {
name: "Requirements"
// 2.1. Promise States
// 2.1.1 When pending, a promise:
function test_pendingFulfilled() {
// 2.1.1.1. may transition to either the fulfilled state
var p = new Promise(function(resolve) {
Qt.callLater(function() {
resolve(42);
});
});
compare(p.isPending(), true);
compare(p.isFulfilled(), false);
compare(p.isRejected(), false);
p.wait();
compare(p.isPending(), false);
compare(p.isFulfilled(), true);
compare(p.isRejected(), false);
}
// 2.1.1 When pending, a promise:
function test_pendingRejected() {
// 2.1.1.1. ... or the rejected state
var p = new Promise(function(resolve, reject) {
Qt.callLater(function() {
reject(new Error(42));
});
});
compare(p.isPending(), true);
compare(p.isFulfilled(), false);
compare(p.isRejected(), false);
p.wait();
compare(p.isPending(), false);
compare(p.isFulfilled(), false);
compare(p.isRejected(), true);
}
// 2.1.2. When fulfilled, a promise:
function test_fulfilled() {
var p = new Promise(function(resolve, reject) {
Qt.callLater(function() {
// 2.1.2.2. must have a value, which must not change.
resolve(42);
resolve(43);
// 2.1.2.1. must not transition to any other state.
reject(new Error("foo"));
});
});
compare(p.isPending(), true);
var value = -1;
var error = null;
p.then(function(res) {
value = res;
}, function(err) {
error = err;
}).wait();
compare(p.isFulfilled(), true);
compare(p.isRejected(), false);
compare(error, null);
compare(value, 42);
}
// 2.1.3 When rejected, a promise:
function test_rejected() {
var p = new Promise(function(resolve, reject) {
Qt.callLater(function() {
// 2.1.3.2. must have a reason, which must not change.
reject(new Error("foo"));
reject(new Error("bar"));
// 2.1.3.1. must not transition to any other state.
resolve(42);
});
});
compare(p.isPending(), true);
var value = -1;
var error = null;
p.then(function(res) {
value = res;
}, function(err) {
error = err;
}).wait();
compare(p.isFulfilled(), false);
compare(p.isRejected(), true);
compare(error instanceof Error, true);
compare(error.message, 'foo');
compare(value, -1);
}
// 2.2. The then Method
// 2.2.1. Both onFulfilled and onRejected are given (resolve)
function test_thenArgsBothResolve() {
var error = null;
var value = -1;
Promise.resolve(42).then(
function(res) { value = res; },
function(err) { error = err; }
).wait();
compare(error, null);
compare(value, 42);
}
// 2.2.1. Both onFulfilled and onRejected are given (reject)
function test_thenArgsBothReject() {
var error = null;
var value = -1;
Promise.reject(new Error('foo')).then(
function(res) { value = res; },
function(err) { error = err; }
).wait();
compare(error instanceof Error, true);
compare(error.message, 'foo');
compare(value, -1);
}
// 2.2.1. onFulfilled is an optional arguments:
function test_thenArgsSecond() {
var error = null;
Promise.reject(new Error('foo')).then(
null,
function(err) { error = err; }
).wait();
compare(error instanceof Error, true);
compare(error.message, 'foo');
}
// 2.2.1. onRejected is an optional arguments:
function test_thenArgsFirst() {
var value = -1;
Promise.resolve(42).then(
function(res) { value = res; }
).wait();
compare(value, 42);
}
// 2.2.1.1. If onFulfilled is not a function, it must be ignored.
function test_thenArgsFirstInvalid() {
var value = -1;
Promise.resolve(42).then('invalid').then(
function(res) { value = res; }
).wait();
compare(value, 42);
}
// 2.2.1.2. If onRejected is not a function, it must be ignored.
function test_thenArgsSecondInvalid() {
var error = -1;
Promise.reject(new Error('foo')).then(
null,
'invalid'
).then(
null,
function(err) { error = err; }
).wait();
compare(error instanceof Error, true);
compare(error.message, 'foo');
}
// 2.2.2. If onFulfilled is a function:
function test_thenOnFulfilled() {
var p0 = new Promise(function(resolve) {
Qt.callLater(function() {
// 2.2.2.3. it must not be called more than once
resolve(42);
resolve(43);
});
});
var values = [];
var p1 = p0.then(function(res) {
values.push(res);
});
// 2.2.2.2. it must not be called before promise is fulfilled.
compare(p0.isPending(), true);
compare(p1.isPending(), true);
compare(values.length, 0);
p1.wait();
// 2.2.2.1. it must be called after promise is fulfilled,
// with promises value as its first argument.
compare(p0.isFulfilled(), true);
compare(p1.isFulfilled(), true);
compare(values, [42]);
}
// 2.2.3. If onRejected is a function:
function test_thenOnRejected() {
var p0 = new Promise(function(resolve, reject) {
Qt.callLater(function() {
// 2.2.2.3. it must not be called more than once
reject(new Error('foo'));
reject(new Error('bar'));
});
});
var errors = [];
var p1 = p0.then(null, function(res) {
errors.push(res);
});
// 2.2.3.2. it must not be called before promise is rejected.
compare(p0.isPending(), true);
compare(p1.isPending(), true);
compare(errors.length, 0);
p1.wait();
// 2.2.3.1. it must be called after promise is rejected,
// with promises reason as its first argument.
compare(p0.isRejected(), true);
compare(p1.isFulfilled(), true);
compare(errors.length, 1);
compare(errors[0] instanceof Error, true);
compare(errors[0].message, 'foo');
}
// 2.2.4. onFulfilled or onRejected must not be called until the execution context
// stack contains only platform code (ie. executed asynchronously, after the event
// loop turn in which then is called, and with a fresh stack).
function test_thenAsynchronous()
{
var value = -1;
var p0 = Promise.resolve(42);
var p1 = p0.then(function(res){ value = res; });
compare(p0.isFulfilled(), true);
compare(p1.isPending(), true);
compare(value, -1);
p1.wait();
compare(p1.isFulfilled(), true);
compare(value, 42);
}
// 2.2.5 onFulfilled and onRejected must be called as functions (i.e. with no this value).
function test_thenThisArg() {
var scopes = [];
Promise.resolve(42).then(function() { scopes.push(this); }).wait();
Promise.reject(new Error('foo')).then(null, function() { scopes.push(this); }).wait();
// Qt doesn't allow to call JS function with undefined "this"
// Let's adopt the sloppy mode (this === the global object).
var global = (function() { return this; })();
compare(scopes, [global, global]);
}
// 2.2.6. then may be called multiple times on the same promise:
// 2.2.6.1. If/when promise is fulfilled, all respective onFulfilled callbacks
// must execute in the order of their originating calls to then:
function test_thenMultipleCalls() {
var values = [];
var p = new Promise(function(resolve) {
Qt.callLater(function() {
resolve(42);
});
});
Promise.all([
p.then(function(res) { return res + 1; }),
p.then(function(res) { return res + 2; }),
p.then(function(res) { return res + 3; })
]).then(function(res) {
values = res;
}).wait();
compare(values, [43, 44, 45]);
}
}
/*
void tst_requirements::thenMultipleCalls()
{
// 2.2.6.2. If/when promise is rejected, all respective onRejected callbacks
// must execute in the order of their originating calls to then:
{
QVector<int> values;
QPromise<int> p([](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
qtpromise_defer([=]() {
reject(8);
});
});
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; })
}).wait();
QCOMPARE(values, QVector<int>({9, 10, 11}));
}
}
void tst_requirements::thenHandlers()
{
// 2.2.7. then must return a promise: p2 = p1.then(onFulfilled, onRejected);
{
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));
}
// 2.2.7.1. If either onFulfilled or onRejected returns a value x, run the
// Promise Resolution Procedure [[Resolve]](p2, x) -> See 2.3.
// 2.2.7.2. If either onFulfilled or onRejected throws an exception e,
// p2 must be rejected with e as the reason.
{
QString reason;
auto p1 = QPromise<int>::resolve(42);
auto p2 = p1.then([](){ throw QString("foo"); });
p2.then(nullptr, [&](const QString& e) { reason = e; }).wait();
QVERIFY(p1.isFulfilled());
QVERIFY(p2.isRejected());
QCOMPARE(reason, QString("foo"));
}
{
QString reason;
auto p1 = QPromise<int>::reject(QString("foo"));
auto p2 = p1.then(nullptr, [](){ throw QString("bar"); return 42; });
p2.then(nullptr, [&](const QString& e) { reason = e; return 0; }).wait();
QVERIFY(p1.isRejected());
QVERIFY(p2.isRejected());
QCOMPARE(reason, QString("bar"));
}
// 2.2.7.3. If onFulfilled is not a function and promise1 is fulfilled,
// p2 must be fulfilled with the same value as promise1
{
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));
p2.then([&](const QString& e) { value = e; }).wait();
QVERIFY(p1.isFulfilled());
QVERIFY(p2.isFulfilled());
QCOMPARE(value, QString("42"));
}
}
*/