1 Commits

Author SHA1 Message Date
50216b65da - wip - 2018-03-15 10:55:45 +01:00
56 changed files with 1233 additions and 940 deletions

1
.gitignore vendored
View File

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

View File

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

View File

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

View File

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

View File

@ -4,9 +4,7 @@
[static] QPromise<T>::all(Sequence<QPromise<T>> promises) -> QPromise<QVector<T>> [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, i.e., at the respective positions to the original sequence, regardless of completion order. 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.
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.) `Sequence` is any STL compatible container (eg. `QVector`, `QList`, `std::vector`, etc.)

View File

@ -1,21 +0,0 @@
## `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.

6
include/QtQmlPromise Normal file
View File

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

View File

@ -1,3 +1,11 @@
INCLUDEPATH += $$PWD/include $$PWD/src INCLUDEPATH += $$PWD/include $$PWD/src
DEPENDPATH += $$PWD/include $$PWD/src DEPENDPATH += $$PWD/include $$PWD/src
CONFIG += c++11 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,10 +1,9 @@
TEMPLATE = subdirs TEMPLATE = subdirs
SUBDIRS = \ SUBDIRS = \
src \
tests tests
_qt_creator_ { tests.depends = src
SUBDIRS += src
}
OTHER_FILES = \ OTHER_FILES = \
package/features/*.prf \ package/features/*.prf \

25
src/imports/imports.pro Normal file
View File

@ -0,0 +1,25 @@
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

5
src/imports/imports.qrc Normal file
View File

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

76
src/imports/plugin.cpp Normal file
View File

@ -0,0 +1,76 @@
// 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

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

5
src/imports/qmldir Normal file
View File

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

View File

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

View File

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

View File

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

View File

@ -2,7 +2,6 @@
#define QTPROMISE_QPROMISEERROR_H #define QTPROMISE_QPROMISEERROR_H
// QtPromise // QtPromise
#include "qpromise_p.h"
#include "qpromiseglobal.h" #include "qpromiseglobal.h"
// Qt // Qt
@ -10,6 +9,53 @@
namespace QtPromise { 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 class QPromiseTimeoutException : public QException
{ {
public: public:
@ -20,12 +66,6 @@ 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 } // namespace QtPromise
#endif // QTPROMISE_QPROMISEERROR_H #endif // QTPROMISE_QPROMISEERROR_H

View File

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

View File

@ -85,7 +85,7 @@ struct ArgsOf<TReturn(T::*)(Args...) const volatile> : public ArgsTraits<Args...
{ }; { };
template <typename T> template <typename T>
struct ArgsOf<std::function<T>> : public ArgsOf<T> struct ArgsOf<std::function<T> > : public ArgsOf<T>
{ }; { };
template <typename 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> 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); return QPromise<T>::all(promises);
} }

View File

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

View File

@ -0,0 +1,175 @@
// 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

@ -0,0 +1,52 @@
#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

@ -0,0 +1,71 @@
#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

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

View File

@ -0,0 +1,8 @@
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

@ -0,0 +1,16 @@
#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,2 +1,5 @@
TEMPLATE = subdirs TEMPLATE = subdirs
SUBDIRS = qtpromise SUBDIRS += \
imports \
qtpromise \
qtqmlpromise

View File

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

View File

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

View File

@ -33,7 +33,7 @@ void tst_helpers::resolve()
int value = -1; int value = -1;
auto p = QtPromise::qPromise(42); 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); QCOMPARE(p.isFulfilled(), true);
@ -49,7 +49,7 @@ void tst_helpers::resolve_void()
int value = -1; int value = -1;
auto p = QtPromise::qPromise(); 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); 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); 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); 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(p.isPending(), true);
QCOMPARE(p0.isFulfilled(), 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(p.isPending(), true);
QCOMPARE(p0.isFulfilled(), 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(p.isPending(), true);
QCOMPARE(p0.isFulfilled(), 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(p.isPending(), true);
QCOMPARE(p0.isFulfilled(), true); QCOMPARE(p0.isFulfilled(), true);
@ -220,9 +220,9 @@ void tst_helpers::allRejected_void()
void tst_helpers::allEmpty() 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); QCOMPARE(p.isFulfilled(), true);
@ -236,9 +236,9 @@ void tst_helpers::allEmpty()
void tst_helpers::allEmpty_void() 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); QCOMPARE(p.isFulfilled(), true);
} }

View File

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

View File

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

View File

@ -1,135 +0,0 @@
// 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,9 +7,6 @@
// Qt // Qt
#include <QtTest> #include <QtTest>
// STL
#include <memory>
using namespace QtPromise; using namespace QtPromise;
class tst_qpromise_construct : public QObject class tst_qpromise_construct : public QObject
@ -33,8 +30,6 @@ private Q_SLOTS:
void rejectSync_void(); void rejectSync_void();
void rejectAsync(); void rejectAsync();
void rejectAsync_void(); void rejectAsync_void();
void connectAndResolve();
void connectAndReject();
}; };
QTEST_MAIN(tst_qpromise_construct) QTEST_MAIN(tst_qpromise_construct)
@ -233,77 +228,3 @@ void tst_qpromise_construct::rejectThrowTwoArgs_void()
QCOMPARE(waitForValue(p, -1, 42), -1); QCOMPARE(waitForValue(p, -1, 42), -1);
QCOMPARE(waitForError(p, QString()), QString("foo")); 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! 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(waitForValue(p, -1), 42);
QCOMPARE(p.isFulfilled(), true); QCOMPARE(p.isFulfilled(), true);
QCOMPARE(value, 8); QCOMPARE(value, 8);
@ -53,7 +53,7 @@ void tst_qpromise_finally::fulfilledSync_void()
return 16; // ignored! 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(waitForValue(p, -1, 42), 42);
QCOMPARE(p.isFulfilled(), true); QCOMPARE(p.isFulfilled(), true);
QCOMPARE(value, 8); QCOMPARE(value, 8);
@ -65,7 +65,7 @@ void tst_qpromise_finally::fulfilledThrows()
throw QString("bar"); 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(waitForError(p, QString()), QString("bar"));
QCOMPARE(p.isRejected(), true); QCOMPARE(p.isRejected(), true);
} }
@ -76,7 +76,7 @@ void tst_qpromise_finally::fulfilledThrows_void()
throw QString("bar"); 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(waitForError(p, QString()), QString("bar"));
QCOMPARE(p.isRejected(), true); QCOMPARE(p.isRejected(), true);
} }
@ -123,7 +123,7 @@ void tst_qpromise_finally::rejectedSync()
return 16; // ignored! 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(waitForError(p, QString()), QString("foo"));
QCOMPARE(p.isRejected(), true); QCOMPARE(p.isRejected(), true);
QCOMPARE(value, 8); QCOMPARE(value, 8);
@ -137,7 +137,7 @@ void tst_qpromise_finally::rejectedSync_void()
return 16; // ignored! 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(waitForError(p, QString()), QString("foo"));
QCOMPARE(p.isRejected(), true); QCOMPARE(p.isRejected(), true);
QCOMPARE(value, 8); QCOMPARE(value, 8);
@ -149,7 +149,7 @@ void tst_qpromise_finally::rejectedThrows()
throw QString("bar"); 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(waitForError(p, QString()), QString("bar"));
QCOMPARE(p.isRejected(), true); QCOMPARE(p.isRejected(), true);
} }
@ -160,7 +160,7 @@ void tst_qpromise_finally::rejectedThrows_void()
throw QString("bar"); 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(waitForError(p, QString()), QString("bar"));
QCOMPARE(p.isRejected(), true); QCOMPARE(p.isRejected(), true);
} }

View File

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

View File

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

View File

@ -1,74 +0,0 @@
// 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,9 +7,6 @@
// Qt // Qt
#include <QtTest> #include <QtTest>
// STL
#include <memory>
using namespace QtPromise; using namespace QtPromise;
class tst_qpromise_resolve : public QObject class tst_qpromise_resolve : public QObject
@ -17,16 +14,14 @@ class tst_qpromise_resolve : public QObject
Q_OBJECT Q_OBJECT
private Q_SLOTS: private Q_SLOTS:
void resolveWithValue(); void value();
void resolveWithNoValue(); void empty_void();
void resolveWithQSharedPtr();
void resolveWithStdSharedPtr();
}; };
QTEST_MAIN(tst_qpromise_resolve) QTEST_MAIN(tst_qpromise_resolve)
#include "tst_resolve.moc" #include "tst_resolve.moc"
void tst_qpromise_resolve::resolveWithValue() void tst_qpromise_resolve::value()
{ {
const int value = 42; const int value = 42;
auto p0 = QPromise<int>::resolve(value); auto p0 = QPromise<int>::resolve(value);
@ -38,49 +33,9 @@ void tst_qpromise_resolve::resolveWithValue()
QCOMPARE(waitForValue(p1, -1), 43); QCOMPARE(waitForValue(p1, -1), 43);
} }
void tst_qpromise_resolve::resolveWithNoValue() void tst_qpromise_resolve::empty_void()
{ {
auto p = QPromise<void>::resolve(); auto p = QPromise<void>::resolve();
QCOMPARE(p.isFulfilled(), true); 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

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

View File

@ -1,159 +0,0 @@
// 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(waitForValue(p, QString()), QString("foo42"));
QCOMPARE(p.isFulfilled(), true); 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(waitForError(p, QString()), QString("foo42"));
QCOMPARE(p.isRejected(), true); QCOMPARE(p.isRejected(), true);
} }
@ -105,7 +105,7 @@ void tst_qpromise_then::skipResult()
value = 43; value = 43;
}).wait(); }).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); QCOMPARE(value, 43);
} }

View File

@ -1,15 +1,10 @@
TEMPLATE = app TEMPLATE = app
CONFIG += testcase warn_on CONFIG += testcase
QT += testlib QT += testlib
QT -= gui QT -= gui
DEFINES += QT_DEPRECATED_WARNINGS 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 { coverage {
gcc { gcc {
message("Code coverage enabled (gcov)") 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 + 1; }),
p.then([&](int r) { values << r + 2; }), p.then([&](int r) { values << r + 2; }),
p.then([&](int r) { values << r + 3; }) 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 + 1; return r + 1; }),
p.then(nullptr, [&](int r) { values << r + 2; return r + 2; }), p.then(nullptr, [&](int r) { values << r + 2; return r + 2; }),
p.then(nullptr, [&](int r) { values << r + 3; return r + 3; }) p.then(nullptr, [&](int r) { values << r + 3; return r + 3; })
@ -314,9 +314,9 @@ void tst_requirements::thenHandlers()
{ {
auto handler = [](){ return 42; }; auto handler = [](){ return 42; };
auto p1 = QPromise<int>::resolve(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(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(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, handler)), QPromise<int> >::value));
} }
// 2.2.7.1. If either onFulfilled or onRejected returns a value x, run the // 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; QString value;
auto p1 = QPromise<QString>::resolve("42"); auto p1 = QPromise<QString>::resolve("42");
auto p2 = p1.then(nullptr, [](){ return QString(); }); 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(); p2.then([&](const QString& e) { value = e; }).wait();
QVERIFY(p1.isFulfilled()); QVERIFY(p1.isFulfilled());

View File

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

View File

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

View File

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

View File

@ -0,0 +1,39 @@
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

@ -0,0 +1,9 @@
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

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

View File

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

View File

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

View File

@ -0,0 +1,384 @@
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"));
}
}
*/