mirror of
https://github.com/simonbrunel/qtpromise.git
synced 2025-07-01 06:41:55 +08:00
Compare commits
29 Commits
Author | SHA1 | Date | |
---|---|---|---|
22faef4ac3 | |||
eebcb4f364 | |||
051fed5fbc | |||
f610826ef0 | |||
4fa7a37750 | |||
69c07855f4 | |||
4cfe2e54f4 | |||
54d88f16a3 | |||
bdf3619469 | |||
8da467e9da | |||
efb6001b9d | |||
26a2110a14 | |||
fa987a5044 | |||
7b0cba5b9d | |||
2c8ed6e676 | |||
d128a5fa8d | |||
dcbb2ef860 | |||
50bae380be | |||
d279fb4391 | |||
313d3882d7 | |||
f794916be6 | |||
9d2a4ca00f | |||
4af2740d80 | |||
18739bd8e0 | |||
c34316243e | |||
d306423159 | |||
d3b69f1248 | |||
36a0eed12a | |||
931d5d5b13 |
22
.appveyor.yml
Normal file
22
.appveyor.yml
Normal file
@ -0,0 +1,22 @@
|
||||
image: Visual Studio 2015
|
||||
|
||||
init:
|
||||
- cmd: call "C:\Program Files (x86)\Microsoft Visual Studio "%VSVER%".0\VC\vcvarsall.bat" %ARCH%
|
||||
|
||||
environment:
|
||||
PATH: '%PATH%;%QTDIR%\bin'
|
||||
matrix:
|
||||
- QTDIR: C:\Qt\5.6\msvc2013
|
||||
VSVER: 12
|
||||
ARCH: x86
|
||||
- QTDIR: C:\Qt\5.9\msvc2013_64
|
||||
VSVER: 12
|
||||
ARCH: x64
|
||||
|
||||
build_script:
|
||||
- cmd: qmake qtpromise.pro
|
||||
- cmd: nmake
|
||||
|
||||
test_script:
|
||||
- cmd: nmake check
|
||||
|
11
.gitignore
vendored
11
.gitignore
vendored
@ -1,7 +1,16 @@
|
||||
_book
|
||||
dist
|
||||
node_modules
|
||||
*.gcno
|
||||
*.gcda
|
||||
*.moc
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
*.user
|
||||
Makefile
|
||||
Makefile*
|
||||
moc_*.cpp
|
||||
moc_*.h
|
||||
coverage.info
|
||||
package-lock.json
|
||||
target_wrapper.bat
|
||||
|
11
.travis.yml
11
.travis.yml
@ -4,12 +4,15 @@ language: cpp
|
||||
compiler: gcc
|
||||
|
||||
before_install:
|
||||
- sudo add-apt-repository -y ppa:beineri/opt-qt542-trusty
|
||||
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
- sudo add-apt-repository -y ppa:beineri/opt-qt563-trusty
|
||||
- sudo apt-get update -qq
|
||||
|
||||
install:
|
||||
- sudo apt-get install -qq qt54base
|
||||
- source /opt/qt54/bin/qt54-env.sh
|
||||
- sudo apt-get install -qq gcc-4.9 g++-4.9
|
||||
- sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.9 60 --slave /usr/bin/g++ g++ /usr/bin/g++-4.9
|
||||
- sudo apt-get install -qq qt56base
|
||||
- source /opt/qt56/bin/qt56-env.sh
|
||||
- wget http://archive.ubuntu.com/ubuntu/pool/universe/l/lcov/lcov_1.13.orig.tar.gz
|
||||
- tar xf lcov_1.13.orig.tar.gz
|
||||
- cd lcov-1.13/
|
||||
@ -19,7 +22,7 @@ install:
|
||||
before_script:
|
||||
- qmake --version
|
||||
- lcov --version
|
||||
- gcc --version
|
||||
- gcc --version && g++ --version
|
||||
|
||||
script:
|
||||
- qmake qtpromise.pro CONFIG+=coverage
|
||||
|
431
README.md
431
README.md
@ -1,431 +1,20 @@
|
||||
<a href="https://promisesaplus.com/" title="Promises/A+ 1.1"><img src="http://promisesaplus.com/assets/logo-small.png" alt="Promises/A+" align="right"/></a>
|
||||
<a href="https://promisesaplus.com/" title="Promises/A+ 1.1"><img src="https://promisesaplus.com/assets/logo-small.png" alt="Promises/A+" align="right"/></a>
|
||||
|
||||
# QtPromise
|
||||
[](http://www.qpm.io/packages/com.github.simonbrunel.qtpromise/index.html) [](https://travis-ci.org/simonbrunel/qtpromise) [](https://codecov.io/gh/simonbrunel/qtpromise)
|
||||
|
||||
[](https://www.qpm.io/packages/com.github.simonbrunel.qtpromise/index.html) [](https://travis-ci.org/simonbrunel/qtpromise) [](https://codecov.io/gh/simonbrunel/qtpromise)
|
||||
|
||||
[Promises/A+](https://promisesaplus.com/) implementation for [Qt/C++](https://www.qt.io/).
|
||||
|
||||
Requires [Qt 5.4](https://www.qt.io/download/) (or later) with [C++11 support enabled](https://wiki.qt.io/How_to_use_C++11_in_your_Qt_Projects).
|
||||
Requires [Qt 5.6](https://www.qt.io/download/) (or later) with [C++11 support enabled](https://wiki.qt.io/How_to_use_C++11_in_your_Qt_Projects).
|
||||
|
||||
## Getting Started
|
||||
### Installation
|
||||
QtPromise is a [header-only](https://en.wikipedia.org/wiki/Header-only) library, simply download the [latest release](https://github.com/simonbrunel/qtpromise/releases/latest) (or [`git submodule`](https://git-scm.com/docs/git-submodule])) and include `qtpromise.pri` from your project `.pro`.
|
||||
## Documentation
|
||||
|
||||
### qpm
|
||||
Alternatively and **only** if your project relies on [qpm](http://www.qpm.io/), you can install QtPromise as follow:
|
||||
|
||||
```bash
|
||||
qpm install com.github.simonbrunel.qtpromise
|
||||
```
|
||||
|
||||
### Usage
|
||||
The recommended way to use QtPromise is to include the single module header:
|
||||
|
||||
```cpp
|
||||
#include <QtPromise>
|
||||
```
|
||||
|
||||
### Example
|
||||
Let's first make the code more readable by using the library namespace:
|
||||
|
||||
```cpp
|
||||
using namespace QtPromise;
|
||||
```
|
||||
|
||||
This `download` function creates a [promise from callbacks](#qpromise-qpromise) which will be resolved when the network request is finished:
|
||||
|
||||
```cpp
|
||||
QPromise<QByteArray> download(const QUrl& url)
|
||||
{
|
||||
return QPromise<QByteArray>([&](
|
||||
const QPromiseResolve<QByteArray>& resolve,
|
||||
const QPromiseReject<QByteArray>& reject) {
|
||||
|
||||
QNetworkReply* reply = manager->get(QNetworkRequest(url));
|
||||
QObject::connect(reply, &QNetworkReply::finished, [=]() {
|
||||
if (reply->error() == QNetworkReply::NoError) {
|
||||
resolve(reply->readAll());
|
||||
} else {
|
||||
reject(reply->error());
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
The following method `uncompress` data in a separate thread and returns a [promise from QFuture](#qtconcurrent):
|
||||
|
||||
```cpp
|
||||
QPromise<Entries> uncompress(const QByteArray& data)
|
||||
{
|
||||
return qPromise(QtConcurrent::run([](const QByteArray& data) {
|
||||
Entries entries;
|
||||
|
||||
// {...} uncompress data and parse content.
|
||||
|
||||
if (error) {
|
||||
throw MalformedException();
|
||||
}
|
||||
|
||||
return entries;
|
||||
}, data));
|
||||
}
|
||||
```
|
||||
|
||||
It's then easy to chain the whole asynchronous process using promises:
|
||||
- initiate the promise chain by downloading a specific URL,
|
||||
- [`then`](#qpromise-then) *and only if download succeeded*, uncompress received data,
|
||||
- [`then`](#qpromise-then) validate and process the uncompressed entries,
|
||||
- [`finally`](#qpromise-finally) perform operations whatever the process succeeded or failed,
|
||||
- and handle specific errors using [`fail`](#qpromise-fail).
|
||||
|
||||
```cpp
|
||||
download(url).then(&uncompress).then([](const Entries& entries) {
|
||||
if (entries.isEmpty()) {
|
||||
throw UpdateException("No entries");
|
||||
}
|
||||
// {...} process entries
|
||||
}).finally([]() {
|
||||
// {...} cleanup
|
||||
}).fail([](QNetworkReply::NetworkError err) {
|
||||
// {...} handle network error
|
||||
}).fail([](const UpdateException& err) {
|
||||
// {...} handle update error
|
||||
}).fail([]() {
|
||||
// {...} catch all
|
||||
});
|
||||
```
|
||||
|
||||
## QtConcurrent
|
||||
QtPromise integrates with [QtConcurrent](http://doc.qt.io/qt-5/qtconcurrent-index.html) to make easy chaining QFuture with QPromise.
|
||||
|
||||
### <a name="qtconcurrent-convert"></a> Convert
|
||||
Converting `QFuture<T>` to `QPromise<T>` is done using the [`qPromise`](#helpers-qpromise) helper:
|
||||
|
||||
```cpp
|
||||
QFuture<int> future = QtConcurrent::run([]() {
|
||||
// {...}
|
||||
return 42;
|
||||
});
|
||||
|
||||
QPromise<int> promise = qPromise(future);
|
||||
```
|
||||
|
||||
or simply:
|
||||
|
||||
```cpp
|
||||
auto promise = qPromise(QtConcurrent::run([]() {
|
||||
// {...}
|
||||
}));
|
||||
```
|
||||
|
||||
### Chain
|
||||
Returning a `QFuture<T>` in [`then`](#qpromise-then) or [`fail`](#qpromise-fail) automatically translate to `QPromise<T>`:
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = ...
|
||||
auto output = input.then([](int res) {
|
||||
return QtConcurrent::run([]() {
|
||||
// {...}
|
||||
return QString("42");
|
||||
});
|
||||
});
|
||||
|
||||
// output type: QPromise<QString>
|
||||
output.then([](const QString& res) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
The `output` promise is resolved when the `QFuture` is [finished](http://doc.qt.io/qt-5/qfuture.html#isFinished).
|
||||
|
||||
### Error
|
||||
Exceptions thrown from a QtConcurrent thread reject the associated promise with the exception as the reason. Note that if you throw an exception that is not a subclass of `QException`, the promise with be rejected with [`QUnhandledException`](http://doc.qt.io/qt-5/qunhandledexception.html#details) (this restriction only applies to exceptions thrown from a QtConcurrent thread, [read more](http://doc.qt.io/qt-5/qexception.html#details)).
|
||||
|
||||
```cpp
|
||||
QPromise<int> promise = ...
|
||||
promise.then([](int res) {
|
||||
return QtConcurrent::run([]() {
|
||||
// {...}
|
||||
|
||||
if (!success) {
|
||||
throw CustomException();
|
||||
}
|
||||
|
||||
return QString("42");
|
||||
});
|
||||
}).fail(const CustomException& err) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
## Thread-Safety
|
||||
QPromise is thread-safe and can be copied and accessed across different threads. QPromise relies on [explicitly data sharing](http://doc.qt.io/qt-5/qexplicitlyshareddatapointer.html#details) and thus `auto p2 = p1` represents the same promise: when `p1` resolves, handlers registered on `p1` and `p2` are called, the fulfilled value being shared between both instances.
|
||||
|
||||
> **Note:** while it's safe to access the resolved value from different threads using [`then`](#qpromise-then), QPromise provides no guarantee about the object being pointed to. Thread-safety and reentrancy rules for that object still apply.
|
||||
|
||||
## QPromise
|
||||
### <a name="qpromise-qpromise"></a> `QPromise<T>::QPromise(resolver)`
|
||||
Creates a new promise that will be fulfilled or rejected by the given `resolver` lambda:
|
||||
|
||||
```cpp
|
||||
QPromise<int> promise([](const QPromiseResolve<int>& resolve, const QPromiseReject<int>& reject) {
|
||||
async_method([=](bool success, int result) {
|
||||
if (success) {
|
||||
resolve(result);
|
||||
} else {
|
||||
reject(customException());
|
||||
}
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
> **Note:** `QPromise<void>` is specialized to not contain any value, meaning that the `resolve` callback takes no argument.
|
||||
|
||||
**C++14**
|
||||
|
||||
```cpp
|
||||
QPromise<int> promise([](const auto& resolve, const auto& reject) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
### <a name="qpromise-then"></a> `QPromise<T>::then(onFulfilled, onRejected) -> QPromise<R>`
|
||||
See [Promises/A+ `.then`](https://promisesaplus.com/#the-then-method) for details.
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = ...
|
||||
auto output = input.then([](int res) {
|
||||
// called with the 'input' result if fulfilled
|
||||
}, [](const ReasonType& reason) {
|
||||
// called with the 'input' reason if rejected
|
||||
// see QPromise<T>::fail for details
|
||||
});
|
||||
```
|
||||
|
||||
> **Note**: `onRejected` handler is optional, `output` will be rejected with the same reason as `input`.
|
||||
|
||||
> **Note**: it's recommended to use the [`fail`](#qpromise-fail) shorthand to handle errors.
|
||||
|
||||
The type `<R>` of the `output` promise depends on the return type of the `onFulfilled` handler:
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = {...}
|
||||
auto output = input.then([](int res) {
|
||||
return QString::number(res); // -> QPromise<QString>
|
||||
});
|
||||
|
||||
// output type: QPromise<QString>
|
||||
output.then([](const QString& res) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
> **Note**: only `onFulfilled` can change the promise type, `onRejected` **must** return the same type as `onFulfilled`. That also means if `onFulfilled` is `nullptr`, `onRejected` must return the same type as the `input` promise.
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = ...
|
||||
auto output = input.then([](int res) {
|
||||
return res + 4;
|
||||
}, [](const ReasonType& reason) {
|
||||
return -1;
|
||||
});
|
||||
```
|
||||
|
||||
If `onFulfilled` doesn't return any value, the `output` type is `QPromise<void>`:
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = ...
|
||||
auto output = input.then([](int res) {
|
||||
// {...}
|
||||
});
|
||||
|
||||
// output type: QPromise<void>
|
||||
output.then([]() {
|
||||
// `QPromise<void>` `onFulfilled` handler has no argument
|
||||
});
|
||||
```
|
||||
|
||||
You can also decide to skip the promise result by omitting the handler argument:
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = {...}
|
||||
auto output = input.then([]( /* skip int result */ ) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
The `output` promise can be *rejected* by throwing an exception in either `onFulfilled` or `onRejected`:
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = {...}
|
||||
auto output = input.then([](int res) {
|
||||
if (res == -1) {
|
||||
throw ReasonType();
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
});
|
||||
|
||||
// output.isRejected() is true
|
||||
```
|
||||
|
||||
If an handler returns a promise (or QFuture), the `output` promise is delayed and will be resolved by the returned promise.
|
||||
|
||||
### <a name="qpromise-fail"></a> `QPromise<T>::fail(onRejected) -> QPromise<T>`
|
||||
Shorthand to `promise.then(nullptr, onRejected)`, similar to the [`catch` statement](http://en.cppreference.com/w/cpp/language/try_catch):
|
||||
|
||||
```cpp
|
||||
promise.fail([](const MyException&) {
|
||||
// {...}
|
||||
}).fail(const QException&) {
|
||||
// {...}
|
||||
}).fail(const std::exception&) {
|
||||
// {...}
|
||||
}).fail() {
|
||||
// {...} catch-all
|
||||
});
|
||||
```
|
||||
|
||||
### <a name="qpromise-finally"></a> `QPromise<T>::finally(handler) -> QPromise<T>`
|
||||
This `handler` is **always** called, without any argument and whatever the `input` promise state (fulfilled or rejected). The `output` promise has the same type as the `input` one but also the same value or error. The finally `handler` **can not modify the fulfilled value** (the returned value is ignored), however, if `handler` throws, `output` is rejected with the new exception.
|
||||
|
||||
```cpp
|
||||
auto output = input.finally([]() {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
### <a name="qpromise-tap"></a> `QPromise<T>::tap(handler) -> QPromise<T>`
|
||||
This `handler` allows to observe the value of the `input` promise, without changing the propagated value. The `output` promise will be resolved with the same value as the `input` promise (the `handler` returned value will be ignored). However, if `handler` throws, `output` is rejected with the new exception. Unlike [`finally`](#qpromise-finally), this handler is **not** called for rejections.
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = {...}
|
||||
auto output = input.tap([](int res) {
|
||||
log(res);
|
||||
}).then([](int res) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
### <a name="qpromise-delay"></a> `QPromise<T>::delay(handler) -> QPromise<T>`
|
||||
This method returns a promise that will be fulfilled with the same value as the `input` promise and after at least `msec` milliseconds. If the `input` promise is rejected, the `output` promise is immediately rejected with the same reason.
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = {...}
|
||||
auto output = input.delay(2000).then([](int res) {
|
||||
// called 2 seconds after `input` is fulfilled
|
||||
});
|
||||
```
|
||||
|
||||
### <a name="qpromise-wait"></a> `QPromise<T>::wait() -> QPromise<T>`
|
||||
This method holds the execution of the remaining code **without** blocking the event loop of the current thread:
|
||||
|
||||
```cpp
|
||||
int result = -1;
|
||||
QPromise<int> input = qPromise(QtConcurrent::run([]() { return 42; }));
|
||||
auto output = input.then([&](int res) {
|
||||
result = res;
|
||||
});
|
||||
|
||||
// output.isPending() is true && result is -1
|
||||
|
||||
output.wait();
|
||||
|
||||
// output.isPending() is false && result is 42
|
||||
```
|
||||
|
||||
### `QPromise<T>::isPending() -> bool`
|
||||
Returns `true` if the promise is pending (not fulfilled or rejected), otherwise returns `false`.
|
||||
|
||||
### `QPromise<T>::isFulfilled() -> bool`
|
||||
Returns `true` if the promise is fulfilled, otherwise returns `false`.
|
||||
|
||||
### `QPromise<T>::isRejected() -> bool`
|
||||
Returns `true` if the promise is rejected, otherwise returns `false`.
|
||||
|
||||
## QPromise (statics)
|
||||
### <a name="qpromise-resolve"></a> `[static] QPromise<T>::resolve(value) -> QPromise<T>`
|
||||
Creates a `QPromise<T>` that is fulfilled with the given `value` of type `T`:
|
||||
|
||||
```cpp
|
||||
QPromise<int> compute(const QString& type)
|
||||
{
|
||||
if (type == "magic") {
|
||||
return QPromise<int>::resolve(42);
|
||||
}
|
||||
|
||||
return QPromise<int>([](const QPromiseResolve<int>& resolve) {
|
||||
// {...}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
See also: [`qPromise`](#helpers-qpromise)
|
||||
|
||||
### <a name="qpromise-reject"></a> `[static] QPromise<T>::reject(reason) -> QPromise<T>`
|
||||
Creates a `QPromise<T>` that is rejected with the given `reason` of *whatever type*:
|
||||
|
||||
```cpp
|
||||
QPromise<int> compute(const QString& type)
|
||||
{
|
||||
if (type == "foobar") {
|
||||
return QPromise<int>::reject(QString("Unknown type: %1").arg(type));
|
||||
}
|
||||
|
||||
return QPromise<int>([](const QPromiseResolve<int>& resolve) {
|
||||
// {...}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### <a name="qpromise-all"></a> `[static] QPromise<T>::all(QVector<QPromise<T>>) -> 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.
|
||||
|
||||
```cpp
|
||||
QVector<QPromise<QByteArray> > promises{
|
||||
download(QUrl("http://a...")),
|
||||
download(QUrl("http://b...")),
|
||||
download(QUrl("http://c..."))
|
||||
};
|
||||
|
||||
auto output = QPromise<QByteArray>::all(promises);
|
||||
|
||||
// output type: QPromise<QVector<QByteArray>>
|
||||
output.then([](const QVector<QByteArray>& res) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
See also: [`qPromiseAll`](#helpers-qpromiseall)
|
||||
|
||||
## Helpers
|
||||
### <a name="helpers-qpromise"></a> `qPromise(T value) -> QPromise<R>`
|
||||
Similar to the `QPromise<T>::resolve` static method, creates a promise resolved from a given `value` without the extra typing:
|
||||
|
||||
```cpp
|
||||
auto promise = qPromise(); // QPromise<void>
|
||||
auto promise = qPromise(42); // QPromise<int>
|
||||
auto promise = qPromise(QString("foo")); // QPromise<QString>
|
||||
```
|
||||
|
||||
This method also allows to convert `QFuture<T>` to `QPromise<T>` delayed until the `QFuture` is finished ([read more](#qtconcurrent-convert)).
|
||||
|
||||
### <a name="helpers-qpromiseall"></a> `qPromiseAll(QVector<QPromise<T> promises) -> QPromise<QVector<T>>`
|
||||
This method simply calls the appropriated [`QPromise<T>::all`](#qpromise-all) static method based on the given `QVector` type. In some cases, this method is more convenient than the static one since it avoid some extra typing:
|
||||
|
||||
```cpp
|
||||
QVector<QPromise<QByteArray> > promises{...}
|
||||
|
||||
auto output = qPromiseAll(promises);
|
||||
// eq. QPromise<QByteArray>::all(promises)
|
||||
```
|
||||
* [Getting Started](https://qtpromise.netlify.com/qtpromise/getting-started.html)
|
||||
* [QtConcurrent](https://qtpromise.netlify.com/qtpromise/qtconcurrent.html)
|
||||
* [Thread-Safety](https://qtpromise.netlify.com/qtpromise/thread-safety.html)
|
||||
* [API Reference](https://qtpromise.netlify.com/qtpromise/api-reference.html)
|
||||
|
||||
## License
|
||||
|
||||
QtPromise is available under the [MIT license](LICENSE).
|
||||
|
54
docs/.vuepress/config.js
Normal file
54
docs/.vuepress/config.js
Normal file
@ -0,0 +1,54 @@
|
||||
module.exports = {
|
||||
title: 'QtPromise',
|
||||
description: 'Promises/A+ implementation for Qt/C++',
|
||||
ga: 'UA-113899811-1',
|
||||
head: [
|
||||
['link', { rel: 'icon', href: `/favicon.png` }],
|
||||
],
|
||||
themeConfig: {
|
||||
repo: 'simonbrunel/qtpromise',
|
||||
lastUpdated: 'Last Updated',
|
||||
editLinks: true,
|
||||
docsDir: 'docs',
|
||||
sidebar: [
|
||||
'qtpromise/getting-started',
|
||||
'qtpromise/qtconcurrent',
|
||||
'qtpromise/thread-safety',
|
||||
'qtpromise/api-reference',
|
||||
{
|
||||
title: 'QPromise',
|
||||
children: [
|
||||
'qtpromise/qpromise/constructor',
|
||||
'qtpromise/qpromise/delay',
|
||||
'qtpromise/qpromise/each',
|
||||
'qtpromise/qpromise/fail',
|
||||
'qtpromise/qpromise/filter',
|
||||
'qtpromise/qpromise/finally',
|
||||
'qtpromise/qpromise/isfulfilled',
|
||||
'qtpromise/qpromise/ispending',
|
||||
'qtpromise/qpromise/isrejected',
|
||||
'qtpromise/qpromise/map',
|
||||
'qtpromise/qpromise/tap',
|
||||
'qtpromise/qpromise/tapfail',
|
||||
'qtpromise/qpromise/then',
|
||||
'qtpromise/qpromise/timeout',
|
||||
'qtpromise/qpromise/wait',
|
||||
'qtpromise/qpromise/all.md',
|
||||
'qtpromise/qpromise/reject.md',
|
||||
'qtpromise/qpromise/resolve.md'
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Helpers',
|
||||
children: [
|
||||
'qtpromise/helpers/attempt',
|
||||
'qtpromise/helpers/each',
|
||||
'qtpromise/helpers/filter',
|
||||
'qtpromise/helpers/map',
|
||||
'qtpromise/helpers/qpromise',
|
||||
'qtpromise/helpers/qpromiseall'
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
2
docs/.vuepress/override.styl
Normal file
2
docs/.vuepress/override.styl
Normal file
@ -0,0 +1,2 @@
|
||||
$accentColor = #23b223
|
||||
$textColor = #404244
|
BIN
docs/.vuepress/public/favicon.ico
Normal file
BIN
docs/.vuepress/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
BIN
docs/.vuepress/public/favicon.png
Normal file
BIN
docs/.vuepress/public/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
4
docs/.vuepress/style.styl
Normal file
4
docs/.vuepress/style.styl
Normal file
@ -0,0 +1,4 @@
|
||||
@import 'override.styl'
|
||||
|
||||
.content a code
|
||||
color: $accentColor
|
9
docs/README.md
Normal file
9
docs/README.md
Normal file
@ -0,0 +1,9 @@
|
||||
<a href="https://promisesaplus.com/" title="Promises/A+ 1.1"><img src="https://promisesaplus.com/assets/logo-small.png" alt="Promises/A+" align="right"/></a>
|
||||
|
||||
# QtPromise
|
||||
[Promises/A+](https://promisesaplus.com/) implementation for [Qt/C++](https://www.qt.io/).
|
||||
|
||||
Requires [Qt 5.6](https://www.qt.io/download/) (or later) with [C++11 support enabled](https://wiki.qt.io/How_to_use_C++11_in_your_Qt_Projects).
|
||||
|
||||
## License
|
||||
QtPromise is available under the [MIT license](https://github.com/simonbrunel/qtpromise/blob/master/LICENSE).
|
34
docs/qtpromise/api-reference.md
Normal file
34
docs/qtpromise/api-reference.md
Normal file
@ -0,0 +1,34 @@
|
||||
# API Reference
|
||||
|
||||
## Functions
|
||||
|
||||
* [`QPromise<T>::QPromise`](qpromise/constructor.md)
|
||||
* [`QPromise<T>::delay`](qpromise/delay.md)
|
||||
* [`QPromise<T>::each`](qpromise/each.md)
|
||||
* [`QPromise<T>::fail`](qpromise/fail.md)
|
||||
* [`QPromise<T>::filter`](qpromise/filter.md)
|
||||
* [`QPromise<T>::finally`](qpromise/finally.md)
|
||||
* [`QPromise<T>::isFulfilled`](qpromise/isfulfilled.md)
|
||||
* [`QPromise<T>::isPending`](qpromise/ispending.md)
|
||||
* [`QPromise<T>::isRejected`](qpromise/isrejected.md)
|
||||
* [`QPromise<T>::map`](qpromise/map.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)
|
||||
|
||||
## Static Functions
|
||||
|
||||
* [`[static] QPromise<T>::all`](qpromise/all.md)
|
||||
* [`[static] QPromise<T>::reject`](qpromise/reject.md)
|
||||
* [`[static] QPromise<T>::resolve`](qpromise/resolve.md)
|
||||
|
||||
## Helpers
|
||||
|
||||
* [`QtPromise::attempt`](helpers/attempt.md)
|
||||
* [`QtPromise::each`](helpers/each.md)
|
||||
* [`QtPromise::filter`](helpers/filter.md)
|
||||
* [`QtPromise::map`](helpers/map.md)
|
||||
* [`qPromise`](helpers/qpromise.md)
|
||||
* [`qPromiseAll`](helpers/qpromiseall.md)
|
95
docs/qtpromise/getting-started.md
Normal file
95
docs/qtpromise/getting-started.md
Normal file
@ -0,0 +1,95 @@
|
||||
# Getting Started
|
||||
|
||||
## Installation
|
||||
|
||||
QtPromise is a [header-only](https://en.wikipedia.org/wiki/Header-only) library, simply download the [latest release](https://github.com/simonbrunel/qtpromise/releases/latest) (or [`git submodule`](https://git-scm.com/docs/git-submodule)) and include `qtpromise.pri` from your project `.pro`.
|
||||
|
||||
### qpm
|
||||
|
||||
Alternatively and **only** if your project relies on [qpm](https://www.qpm.io/), you can install QtPromise as follow:
|
||||
|
||||
```bash
|
||||
qpm install com.github.simonbrunel.qtpromise
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
The recommended way to use QtPromise is to include the single module header:
|
||||
|
||||
```cpp
|
||||
#include <QtPromise>
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
Let's first make the code more readable by using the library namespace:
|
||||
|
||||
```cpp
|
||||
using namespace QtPromise;
|
||||
```
|
||||
|
||||
This `download` function creates a [promise from callbacks](qpromise/constructor.md) which will be resolved when the network request is finished:
|
||||
|
||||
```cpp
|
||||
QPromise<QByteArray> download(const QUrl& url)
|
||||
{
|
||||
return QPromise<QByteArray>([&](
|
||||
const QPromiseResolve<QByteArray>& resolve,
|
||||
const QPromiseReject<QByteArray>& reject) {
|
||||
|
||||
QNetworkReply* reply = manager->get(QNetworkRequest(url));
|
||||
QObject::connect(reply, &QNetworkReply::finished, [=]() {
|
||||
if (reply->error() == QNetworkReply::NoError) {
|
||||
resolve(reply->readAll());
|
||||
} else {
|
||||
reject(reply->error());
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
The following method `uncompress` data in a separate thread and returns a [promise from QFuture](qtconcurrent.md):
|
||||
|
||||
```cpp
|
||||
QPromise<Entries> uncompress(const QByteArray& data)
|
||||
{
|
||||
return qPromise(QtConcurrent::run([](const QByteArray& data) {
|
||||
Entries entries;
|
||||
|
||||
// {...} uncompress data and parse content.
|
||||
|
||||
if (error) {
|
||||
throw MalformedException();
|
||||
}
|
||||
|
||||
return entries;
|
||||
}, data));
|
||||
}
|
||||
```
|
||||
|
||||
It's then easy to chain the whole asynchronous process using promises:
|
||||
- initiate the promise chain by downloading a specific URL,
|
||||
- [`then`](qpromise/then.md) *and only if download succeeded*, uncompress received data,
|
||||
- [`then`](qpromise/then.md) validate and process the uncompressed entries,
|
||||
- [`finally`](qpromise/finally.md) perform operations whatever the process succeeded or failed,
|
||||
- and handle specific errors using [`fail`](qpromise/fail.md).
|
||||
|
||||
```cpp
|
||||
download(url).then(&uncompress).then([](const Entries& entries) {
|
||||
if (entries.isEmpty()) {
|
||||
throw UpdateException("No entries");
|
||||
}
|
||||
// {...} process entries
|
||||
}).finally([]() {
|
||||
// {...} cleanup
|
||||
}).fail([](QNetworkReply::NetworkError err) {
|
||||
// {...} handle network error
|
||||
}).fail([](const UpdateException& err) {
|
||||
// {...} handle update error
|
||||
}).fail([]() {
|
||||
// {...} catch all
|
||||
});
|
||||
```
|
47
docs/qtpromise/helpers/attempt.md
Normal file
47
docs/qtpromise/helpers/attempt.md
Normal file
@ -0,0 +1,47 @@
|
||||
---
|
||||
title: attempt
|
||||
---
|
||||
|
||||
# QtPromise::attempt
|
||||
|
||||
*Since: 0.4.0*
|
||||
|
||||
```cpp
|
||||
QtPromise::attempt(Functor functor, Args...) -> QPromise<R>
|
||||
|
||||
// With:
|
||||
// - Functor: Function(Args...) -> R | QPromise<R>
|
||||
```
|
||||
|
||||
Calls `functor` immediately and returns a promise fulfilled with the value returned by
|
||||
`functor`. Any synchronous exceptions will be turned into rejections on the returned
|
||||
promise. This is a convenient method that can be used instead of handling both synchronous
|
||||
and asynchronous exception flows.
|
||||
|
||||
The type `R` of the `output` promise depends on the type returned by the `functor` function.
|
||||
If `functor` returns a promise (or `QFuture`), the `output` promise is delayed and will be
|
||||
resolved by the returned promise.
|
||||
|
||||
```cpp
|
||||
QPromise<QByteArray> download(const QUrl& url);
|
||||
|
||||
QPromise<QByteArray> process(const QUrl& url)
|
||||
{
|
||||
return QtPromise::attempt([&]() {
|
||||
if (!url.isValid()) {
|
||||
throw InvalidUrlException();
|
||||
}
|
||||
|
||||
return download(url);
|
||||
}
|
||||
}
|
||||
|
||||
auto output = process(url);
|
||||
|
||||
// 'output' type: QPromise<QByteArray>
|
||||
output.then([](const QByteArray& res) {
|
||||
// {...}
|
||||
}).fail([](const InvalidUrlException& err) {
|
||||
// {...}
|
||||
});
|
||||
```
|
45
docs/qtpromise/helpers/each.md
Normal file
45
docs/qtpromise/helpers/each.md
Normal file
@ -0,0 +1,45 @@
|
||||
---
|
||||
title: each
|
||||
---
|
||||
|
||||
# QtPromise::each
|
||||
|
||||
*Since: 0.4.0*
|
||||
|
||||
```cpp
|
||||
QtPromise::each(Sequence<T> values, Functor functor) -> QPromise<Sequence<T>>
|
||||
|
||||
// With:
|
||||
// - Sequence: STL compatible container (e.g. QVector, etc.)
|
||||
// - Functor: Function(T value, int index) -> void | QPromise<void>
|
||||
```
|
||||
|
||||
Calls the given `functor` on each element in `values` then resolves to the original sequence
|
||||
unmodified. If `functor` throws, `output` is rejected with the new exception.
|
||||
|
||||
If `functor` returns a promise (or `QFuture`), the `output` promise is delayed until all the
|
||||
promises are resolved. If any of the promises fail, `output` immediately rejects with the error
|
||||
of the promise that rejected, whether or not the other promises are resolved.
|
||||
|
||||
|
||||
```cpp
|
||||
auto output = QtPromise::each(QVector<QUrl>{
|
||||
QUrl("http://a..."),
|
||||
QUrl("http://b..."),
|
||||
QUrl("http://c...")
|
||||
}, [](const QUrl& url, ...) {
|
||||
return QPromise<void>([&](auto resolve, auto reject) {
|
||||
// process url asynchronously ...
|
||||
})
|
||||
});
|
||||
|
||||
// `output` resolves as soon as all promises returned by
|
||||
// `functor` are fulfilled or at least one is rejected.
|
||||
|
||||
// output type: QPromise<QVector<QUrl>>
|
||||
output.then([](const QVector<QUrl>& res) {
|
||||
// 'res' contains the original values
|
||||
});
|
||||
```
|
||||
|
||||
See also: [`QPromise<T>::each`](../qpromise/each.md)
|
52
docs/qtpromise/helpers/filter.md
Normal file
52
docs/qtpromise/helpers/filter.md
Normal file
@ -0,0 +1,52 @@
|
||||
---
|
||||
title: filter
|
||||
---
|
||||
|
||||
# QtPromise::filter
|
||||
|
||||
*Since: 0.4.0*
|
||||
|
||||
```cpp
|
||||
QtPromise::filter(Sequence<T> values, Filterer filterer) -> QPromise<Sequence<T>>
|
||||
|
||||
// With:
|
||||
// - Sequence: STL compatible container (e.g. QVector, etc.)
|
||||
// - Filterer: Function(T value, int index) -> bool
|
||||
```
|
||||
|
||||
Iterates over `values` and [filters the sequence](https://en.wikipedia.org/wiki/Filter_%28higher-order_function%29)
|
||||
to another using the given `filterer` function. If `filterer` returns `true`, a copy of the item
|
||||
is put in the `output` sequence, otherwise, the item will not appear in `output`. If `filterer`
|
||||
throws, `output` is rejected with the new exception.
|
||||
|
||||
If `filterer` returns a promise (or `QFuture`), the `output` promise is delayed until all the
|
||||
promises are resolved. If any of the promises fail, `output` immediately rejects with the error
|
||||
of the promise that rejected, whether or not the other promises are resolved.
|
||||
|
||||
```cpp
|
||||
auto output = QtPromise::filter(QVector{
|
||||
QUrl("http://a..."),
|
||||
QUrl("http://b..."),
|
||||
QUrl("http://c...")
|
||||
}, [](const QUrl& url, ...) {
|
||||
return QPromise<bool>([&](auto resolve, auto reject) {
|
||||
// resolve(true) if 'url' is reachable, else resolve(false)
|
||||
// {...}
|
||||
});
|
||||
});
|
||||
|
||||
// 'output' resolves as soon as all promises returned by
|
||||
// 'filterer' are fulfilled or at least one is rejected.
|
||||
|
||||
// 'output' type: QPromise<QVector<QUrl>>
|
||||
output.then([](const QVector<QUrl>& res) {
|
||||
// 'res' contains only reachable URLs
|
||||
});
|
||||
```
|
||||
|
||||
::: tip NOTE
|
||||
The order of the output sequence values is guarantee to be the same as the original
|
||||
sequence, regardless of completion order of the promises returned by `filterer`.
|
||||
:::
|
||||
|
||||
See also: [`QPromise<T>::filter`](../qpromise/filter.md)
|
51
docs/qtpromise/helpers/map.md
Normal file
51
docs/qtpromise/helpers/map.md
Normal file
@ -0,0 +1,51 @@
|
||||
---
|
||||
title: map
|
||||
---
|
||||
|
||||
# QtPromise::map
|
||||
|
||||
*Since: 0.4.0*
|
||||
|
||||
```cpp
|
||||
QtPromise::map(Sequence<T> values, Mapper mapper) -> QPromise<QVector<R>>
|
||||
|
||||
// With:
|
||||
// - Sequence: STL compatible container (e.g. QVector, etc.)
|
||||
// - Mapper: Function(T value, int index) -> R | QPromise<R>
|
||||
```
|
||||
|
||||
Iterates over `values` and [maps the sequence](https://en.wikipedia.org/wiki/Map_%28higher-order_function%29)
|
||||
to another using the given `mapper` function. The type returned by `mapper` determines the type
|
||||
of the `output` promise. If `mapper` throws, `output` is rejected with the new exception.
|
||||
|
||||
If `mapper` returns a promise (or `QFuture`), the `output` promise is delayed until all the
|
||||
promises are resolved. If any of the promises fails, `output` immediately rejects with the
|
||||
error of the promise that rejected, whether or not the other promises are resolved.
|
||||
|
||||
```cpp
|
||||
auto output = QtPromise::map(QVector{
|
||||
QUrl("http://a..."),
|
||||
QUrl("http://b..."),
|
||||
QUrl("http://c...")
|
||||
}, [](const QUrl& url, ...) {
|
||||
return QPromise<QByteArray>([&](auto resolve, auto reject) {
|
||||
// download content at url and resolve
|
||||
// {...}
|
||||
});
|
||||
});
|
||||
|
||||
// 'output' resolves as soon as all promises returned by
|
||||
// 'mapper' are fulfilled or at least one is rejected.
|
||||
|
||||
// 'output' type: QPromise<QVector<QByteArray>>
|
||||
output.then([](const QVector<QByteArray>& res) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
::: tip NOTE
|
||||
The order of the output sequence values is guarantee to be the same as the original
|
||||
sequence, regardless of completion order of the promises returned by `mapper`.
|
||||
:::
|
||||
|
||||
See also: [`QPromise<T>::map`](../qpromise/map.md)
|
21
docs/qtpromise/helpers/qpromise.md
Normal file
21
docs/qtpromise/helpers/qpromise.md
Normal file
@ -0,0 +1,21 @@
|
||||
---
|
||||
title: qPromise
|
||||
---
|
||||
|
||||
# qPromise
|
||||
|
||||
*Since: 0.1.0*
|
||||
|
||||
```
|
||||
qPromise(T value) -> QPromise<R>
|
||||
```
|
||||
|
||||
Similar to the [`QPromise<T>::resolve`](../qpromise/resolve.md) static method, creates a promise resolved from a given `value` without the extra typing:
|
||||
|
||||
```cpp
|
||||
auto promise = qPromise(); // QPromise<void>
|
||||
auto promise = qPromise(42); // QPromise<int>
|
||||
auto promise = qPromise(QString("foo")); // QPromise<QString>
|
||||
```
|
||||
|
||||
This method also allows to convert `QFuture<T>` to `QPromise<T>` delayed until the `QFuture` is finished ([read more](../qtconcurrent.md#convert)).
|
22
docs/qtpromise/helpers/qpromiseall.md
Normal file
22
docs/qtpromise/helpers/qpromiseall.md
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
title: qPromiseAll
|
||||
---
|
||||
|
||||
# qPromiseAll
|
||||
|
||||
*Since: 0.1.0*
|
||||
|
||||
```
|
||||
qPromiseAll(Sequence<QPromise<T>> promises) -> QPromise<QVector<T>>
|
||||
qPromiseAll(Sequence<QPromise<void>> promises) -> QPromise<void>
|
||||
```
|
||||
|
||||
This method simply calls the appropriated [`QPromise<T>::all`](../qpromise/all.md) static method based on the given `QVector` type. In some cases, this method is more convenient than the static one since it avoid some extra typing:
|
||||
|
||||
```cpp
|
||||
QVector<QPromise<QByteArray> > promises{...}
|
||||
|
||||
auto output = qPromiseAll(promises);
|
||||
// eq. QPromise<QByteArray>::all(promises)
|
||||
```
|
||||
|
34
docs/qtpromise/qpromise/all.md
Normal file
34
docs/qtpromise/qpromise/all.md
Normal file
@ -0,0 +1,34 @@
|
||||
---
|
||||
title: ::all [static]
|
||||
---
|
||||
|
||||
# QPromise::all [static]
|
||||
|
||||
*Since: 0.1.0*
|
||||
|
||||
```
|
||||
[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.
|
||||
|
||||
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.)
|
||||
|
||||
```cpp
|
||||
QVector<QPromise<QByteArray> > promises{
|
||||
download(QUrl("http://a...")),
|
||||
download(QUrl("http://b...")),
|
||||
download(QUrl("http://c..."))
|
||||
};
|
||||
|
||||
auto output = QPromise<QByteArray>::all(promises);
|
||||
|
||||
// output type: QPromise<QVector<QByteArray>>
|
||||
output.then([](const QVector<QByteArray>& res) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
See also: [`qPromiseAll`](../helpers/qpromiseall.md)
|
37
docs/qtpromise/qpromise/constructor.md
Normal file
37
docs/qtpromise/qpromise/constructor.md
Normal file
@ -0,0 +1,37 @@
|
||||
---
|
||||
title: constructor
|
||||
---
|
||||
|
||||
# QPromise::QPromise
|
||||
|
||||
*Since: 0.1.0*
|
||||
|
||||
```cpp
|
||||
QPromise<T>::QPromise(Function resolver)
|
||||
```
|
||||
|
||||
Creates a new promise that will be fulfilled or rejected by the given `resolver` lambda:
|
||||
|
||||
```cpp
|
||||
QPromise<int> promise([](const QPromiseResolve<int>& resolve, const QPromiseReject<int>& reject) {
|
||||
async_method([=](bool success, int result) {
|
||||
if (success) {
|
||||
resolve(result);
|
||||
} else {
|
||||
reject(customException());
|
||||
}
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
::: tip NOTE
|
||||
`QPromise<void>` is specialized to not contain any value, meaning that the `resolve` callback takes no argument.
|
||||
:::
|
||||
|
||||
**C++14**
|
||||
|
||||
```cpp
|
||||
QPromise<int> promise([](const auto& resolve, const auto& reject) {
|
||||
// {...}
|
||||
});
|
||||
```
|
20
docs/qtpromise/qpromise/delay.md
Normal file
20
docs/qtpromise/qpromise/delay.md
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
title: .delay
|
||||
---
|
||||
|
||||
# QPromise::delay
|
||||
|
||||
*Since: 0.2.0*
|
||||
|
||||
```cpp
|
||||
QPromise<T>::delay(int msec) -> QPromise<T>
|
||||
```
|
||||
|
||||
This method returns a promise that will be fulfilled with the same value as the `input` promise and after at least `msec` milliseconds. If the `input` promise is rejected, the `output` promise is immediately rejected with the same reason.
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = {...}
|
||||
auto output = input.delay(2000).then([](int res) {
|
||||
// called 2 seconds after `input` is fulfilled
|
||||
});
|
||||
```
|
56
docs/qtpromise/qpromise/each.md
Normal file
56
docs/qtpromise/qpromise/each.md
Normal file
@ -0,0 +1,56 @@
|
||||
---
|
||||
title: .each
|
||||
---
|
||||
|
||||
# QPromise::each
|
||||
|
||||
*Since: 0.4.0*
|
||||
|
||||
```cpp
|
||||
QPromise<Sequence<T>>::each(Functor functor) -> QPromise<Sequence<T>>
|
||||
|
||||
// With:
|
||||
// - Sequence: STL compatible container
|
||||
// - Functor: Function(T value, int index) -> any
|
||||
```
|
||||
|
||||
::: warning IMPORTANT
|
||||
This method only applies to promise with sequence value.
|
||||
:::
|
||||
|
||||
Calls the given `functor` on each element in the promise value (i.e. `Sequence<T>`), then resolves to the original sequence unmodified. If `functor` throws, `output` is rejected with the new exception.
|
||||
|
||||
```cpp
|
||||
QPromise<QList<QByteArray>> input = {...}
|
||||
|
||||
auto output = input.each([](const QByteArray& value, int index) {
|
||||
// process value ...
|
||||
});
|
||||
|
||||
// output type: QPromise<QList<QByteArray>>
|
||||
output.then([](const QList<QByteArray>& res) {
|
||||
// 'res' contains the original values
|
||||
});
|
||||
```
|
||||
|
||||
If `functor` returns a promise (or `QFuture`), the `output` promise is delayed until all the promises are resolved. If any of the promises fail, `output` immediately rejects with the error of the promise that rejected, whether or not the other promises are resolved.
|
||||
|
||||
```cpp
|
||||
QPromise<QList<QUrl>> input = {...}
|
||||
|
||||
auto output = input.each([](const QUrl& url, ...) {
|
||||
return QPromise<void>([&](auto resolve, auto reject) {
|
||||
// process url asynchronously ...
|
||||
})
|
||||
});
|
||||
|
||||
// `output` resolves as soon as all promises returned by
|
||||
// `functor` are fulfilled or at least one is rejected.
|
||||
|
||||
// output type: QPromise<QList<QUrl>>
|
||||
output.then([](const QList<QUrl>& res) {
|
||||
// 'res' contains the original values
|
||||
});
|
||||
```
|
||||
|
||||
See also: [`QtPromise::each`](../helpers/each.md)
|
25
docs/qtpromise/qpromise/fail.md
Normal file
25
docs/qtpromise/qpromise/fail.md
Normal file
@ -0,0 +1,25 @@
|
||||
---
|
||||
title: .fail
|
||||
---
|
||||
|
||||
# QPromise::fail
|
||||
|
||||
*Since: 0.1.0*
|
||||
|
||||
```cpp
|
||||
QPromise<T>::fail(Function onRejected) -> QPromise<T>
|
||||
```
|
||||
|
||||
Shorthand to `promise.then(nullptr, onRejected)`, similar to the [`catch` statement](http://en.cppreference.com/w/cpp/language/try_catch):
|
||||
|
||||
```cpp
|
||||
promise.fail([](const MyException&) {
|
||||
// {...}
|
||||
}).fail(const QException&) {
|
||||
// {...}
|
||||
}).fail(const std::exception&) {
|
||||
// {...}
|
||||
}).fail() {
|
||||
// {...} catch-all
|
||||
});
|
||||
```
|
55
docs/qtpromise/qpromise/filter.md
Normal file
55
docs/qtpromise/qpromise/filter.md
Normal file
@ -0,0 +1,55 @@
|
||||
---
|
||||
title: .filter
|
||||
---
|
||||
|
||||
# QPromise::filter
|
||||
|
||||
*Since: 0.4.0*
|
||||
|
||||
```cpp
|
||||
QPromise<Sequence<T>>::filter(Filter filterer) -> QPromise<Sequence<T>>
|
||||
|
||||
// With:
|
||||
// - Sequence: STL compatible container (e.g. QVector, etc.)
|
||||
// - Filterer: Function(T value, int index) -> bool
|
||||
```
|
||||
|
||||
::: warning IMPORTANT
|
||||
This method only applies to promise with sequence value.
|
||||
:::
|
||||
|
||||
Iterates over all the promise values (i.e. `Sequence<T>`) and [filters the sequence](https://en.wikipedia.org/wiki/Filter_%28higher-order_function%29)
|
||||
to another using the given `filterer` function. If `filterer` returns `true`, a copy of the item
|
||||
is put in the `output` sequence, otherwise, the item will not appear in `output`. If `filterer`
|
||||
throws, `output` is rejected with the new exception.
|
||||
|
||||
If `filterer` returns a promise (or `QFuture`), the `output` promise is delayed until all the
|
||||
promises are resolved. If any of the promises fail, `output` immediately rejects with the error
|
||||
of the promise that rejected, whether or not the other promises are resolved.
|
||||
|
||||
```cpp
|
||||
QPromise<QList<QUrl>> input = {...}
|
||||
|
||||
auto output = input.filter([](const QUrl& url, ...) {
|
||||
return url.isValid(); // Keep only valid URLs
|
||||
}).filter([](const QUrl& url, ...) {
|
||||
return QPromise<bool>([&](auto resolve, auto reject) {
|
||||
// resolve(true) if `url` is reachable, else resolve(false)
|
||||
});
|
||||
});
|
||||
|
||||
// 'output' resolves as soon as all promises returned by
|
||||
// 'filterer' are fulfilled or at least one is rejected.
|
||||
|
||||
// 'output' type: QPromise<QList<QUrl>>
|
||||
output.then([](const QList<QUrl>& res) {
|
||||
// 'res' contains only reachable URLs
|
||||
});
|
||||
```
|
||||
|
||||
::: tip NOTE
|
||||
The order of the output sequence values is guarantee to be the same as the original
|
||||
sequence, regardless of completion order of the promises returned by `filterer`.
|
||||
:::
|
||||
|
||||
See also: [`QtPromise::filter`](../helpers/filter.md)
|
21
docs/qtpromise/qpromise/finally.md
Normal file
21
docs/qtpromise/qpromise/finally.md
Normal file
@ -0,0 +1,21 @@
|
||||
---
|
||||
title: .finally
|
||||
---
|
||||
|
||||
# QPromise::finally
|
||||
|
||||
*Since: 0.1.0*
|
||||
|
||||
```cpp
|
||||
QPromise<T>::finally(Function handler) -> QPromise<T>
|
||||
```
|
||||
|
||||
This `handler` is **always** called, without any argument and whatever the `input` promise state (fulfilled or rejected). The `output` promise has the same type as the `input` one but also the same value or error. The finally `handler` **can not modify the fulfilled value** (the returned value is ignored), however, if `handler` throws, `output` is rejected with the new exception.
|
||||
|
||||
```cpp
|
||||
auto output = input.finally([]() {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
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.
|
13
docs/qtpromise/qpromise/isfulfilled.md
Normal file
13
docs/qtpromise/qpromise/isfulfilled.md
Normal file
@ -0,0 +1,13 @@
|
||||
---
|
||||
title: .isFulfilled
|
||||
---
|
||||
|
||||
# QPromise::isFulfilled
|
||||
|
||||
*Since: 0.1.0*
|
||||
|
||||
```cpp
|
||||
QPromise<T>::isFulfilled() -> bool
|
||||
```
|
||||
|
||||
Returns `true` if the promise is fulfilled, otherwise returns `false`.
|
13
docs/qtpromise/qpromise/ispending.md
Normal file
13
docs/qtpromise/qpromise/ispending.md
Normal file
@ -0,0 +1,13 @@
|
||||
---
|
||||
title: .isPending
|
||||
---
|
||||
|
||||
# QPromise::isPending
|
||||
|
||||
*Since: 0.1.0*
|
||||
|
||||
```cpp
|
||||
QPromise<T>::isPending() -> bool
|
||||
```
|
||||
|
||||
Returns `true` if the promise is pending (not fulfilled or rejected), otherwise returns `false`.
|
14
docs/qtpromise/qpromise/isrejected.md
Normal file
14
docs/qtpromise/qpromise/isrejected.md
Normal file
@ -0,0 +1,14 @@
|
||||
---
|
||||
title: .isRejected
|
||||
---
|
||||
|
||||
# QPromise::isRejected
|
||||
|
||||
*Since: 0.1.0*
|
||||
|
||||
```cpp
|
||||
QPromise<T>::isRejected() -> bool
|
||||
```
|
||||
|
||||
Returns `true` if the promise is rejected, otherwise returns `false`.
|
||||
|
67
docs/qtpromise/qpromise/map.md
Normal file
67
docs/qtpromise/qpromise/map.md
Normal file
@ -0,0 +1,67 @@
|
||||
---
|
||||
title: .map
|
||||
---
|
||||
|
||||
# QPromise::map
|
||||
|
||||
*Since: 0.4.0*
|
||||
|
||||
```cpp
|
||||
QPromise<Sequence<T>>::map(Mapper mapper) -> QPromise<QVector<R>>
|
||||
|
||||
// With:
|
||||
// - Sequence: STL compatible container (e.g. QVector, etc.)
|
||||
// - Mapper: Function(T value, int index) -> R | QPromise<R>
|
||||
```
|
||||
|
||||
::: warning IMPORTANT
|
||||
This method only applies to promise with sequence value.
|
||||
:::
|
||||
|
||||
Iterates over all the promise values (i.e. `Sequence<T>`) and [maps the sequence](https://en.wikipedia.org/wiki/Map_%28higher-order_function%29)
|
||||
to another using the given `mapper` function. The type returned by `mapper` determines the type
|
||||
of the `output` promise. If `mapper` throws, `output` is rejected with the new exception.
|
||||
|
||||
If `mapper` returns a promise (or `QFuture`), the `output` promise is delayed until all the
|
||||
promises are resolved. If any of the promises fails, `output` immediately rejects with the
|
||||
error of the promise that rejected, whether or not the other promises are resolved.
|
||||
|
||||
```cpp
|
||||
QPromise<QList<QUrl>> input = {...}
|
||||
|
||||
auto output = input.map([](const QUrl& url, int index) {
|
||||
return QPromise<QByteArray>([&](auto resolve, auto reject) {
|
||||
// download content at 'url' and resolve
|
||||
// {...}
|
||||
});
|
||||
}).map([](const QByteArray& value, ...) {
|
||||
// process the downloaded QByteArray
|
||||
// {...}
|
||||
return DownloadResult(value);
|
||||
});
|
||||
|
||||
// 'output' resolves as soon as all promises returned by
|
||||
// 'mapper' are fulfilled or at least one is rejected.
|
||||
|
||||
// 'output' type: QPromise<QVector<DownloadResult>>
|
||||
output.then([](const QVector<DownloadResult>& res) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
::: tip NOTE
|
||||
The order of the output sequence values is guarantee to be the same as the original
|
||||
sequence, regardless of completion order of the promises returned by `mapper`.
|
||||
:::
|
||||
|
||||
This function is provided for convenience and is similar to:
|
||||
|
||||
```cpp
|
||||
promise.then([](const Sequence<T>& values) {
|
||||
return QtPromise::map(values, [](const T& value, int index) {
|
||||
return // {...}
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
See also: [`QtPromise::map`](../helpers/map.md)
|
26
docs/qtpromise/qpromise/reject.md
Normal file
26
docs/qtpromise/qpromise/reject.md
Normal file
@ -0,0 +1,26 @@
|
||||
---
|
||||
title: ::reject [static]
|
||||
---
|
||||
|
||||
# QPromise::reject [static]
|
||||
|
||||
*Since: 0.1.0*
|
||||
|
||||
```cpp
|
||||
[static] QPromise<T>::reject(any reason) -> QPromise<T>
|
||||
```
|
||||
|
||||
Creates a `QPromise<T>` that is rejected with the given `reason` of *whatever type*:
|
||||
|
||||
```cpp
|
||||
QPromise<int> compute(const QString& type)
|
||||
{
|
||||
if (type == "foobar") {
|
||||
return QPromise<int>::reject(QString("Unknown type: %1").arg(type));
|
||||
}
|
||||
|
||||
return QPromise<int>([](const QPromiseResolve<int>& resolve) {
|
||||
// {...}
|
||||
});
|
||||
}
|
||||
```
|
28
docs/qtpromise/qpromise/resolve.md
Normal file
28
docs/qtpromise/qpromise/resolve.md
Normal file
@ -0,0 +1,28 @@
|
||||
---
|
||||
title: ::resolve [static]
|
||||
---
|
||||
|
||||
# QPromise::resolve [static]
|
||||
|
||||
*Since: 0.1.0*
|
||||
|
||||
```
|
||||
[static] QPromise<T>::resolve(T value) -> QPromise<T>
|
||||
```
|
||||
|
||||
Creates a `QPromise<T>` that is fulfilled with the given `value` of type `T`:
|
||||
|
||||
```cpp
|
||||
QPromise<int> compute(const QString& type)
|
||||
{
|
||||
if (type == "magic") {
|
||||
return QPromise<int>::resolve(42);
|
||||
}
|
||||
|
||||
return QPromise<int>([](const QPromiseResolve<int>& resolve) {
|
||||
// {...}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
See also: [`qPromise`](../helpers/qpromise.md)
|
24
docs/qtpromise/qpromise/tap.md
Normal file
24
docs/qtpromise/qpromise/tap.md
Normal file
@ -0,0 +1,24 @@
|
||||
---
|
||||
title: .tap
|
||||
---
|
||||
|
||||
# QPromise::tap
|
||||
|
||||
*Since: 0.2.0*
|
||||
|
||||
```cpp
|
||||
QPromise<T>::tap(Function handler) -> QPromise<T>
|
||||
```
|
||||
|
||||
This `handler` allows to observe the value of the `input` promise, without changing the propagated value. The `output` promise will be resolved with the same value as the `input` promise (the `handler` returned value will be ignored). However, if `handler` throws, `output` is rejected with the new exception. Unlike [`finally`](finally.md), this handler is **not** called for rejections.
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = {...}
|
||||
auto output = input.tap([](int res) {
|
||||
log(res);
|
||||
}).then([](int res) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
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.
|
27
docs/qtpromise/qpromise/tapfail.md
Normal file
27
docs/qtpromise/qpromise/tapfail.md
Normal file
@ -0,0 +1,27 @@
|
||||
---
|
||||
title: .tapFail
|
||||
---
|
||||
|
||||
# QPromise::tapFail
|
||||
|
||||
*Since: 0.4.0*
|
||||
|
||||
```cpp
|
||||
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.
|
99
docs/qtpromise/qpromise/then.md
Normal file
99
docs/qtpromise/qpromise/then.md
Normal file
@ -0,0 +1,99 @@
|
||||
---
|
||||
title: .then
|
||||
---
|
||||
|
||||
# QPromise::then
|
||||
|
||||
*Since: 0.1.0*
|
||||
|
||||
```cpp
|
||||
QPromise<T>::then(Function onFulfilled, Function onRejected) -> QPromise<R>
|
||||
QPromise<T>::then(Function onFulfilled) -> QPromise<R>
|
||||
```
|
||||
|
||||
See [Promises/A+ `.then`](https://promisesaplus.com/#the-then-method) for details.
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = ...
|
||||
auto output = input.then([](int res) {
|
||||
// called with the 'input' result if fulfilled
|
||||
}, [](const ReasonType& reason) {
|
||||
// called with the 'input' reason if rejected
|
||||
// see QPromise<T>::fail for details
|
||||
});
|
||||
```
|
||||
|
||||
::: tip NOTE
|
||||
`onRejected` handler is optional, in which case `output` will be rejected with
|
||||
the same reason as `input`. Also note that it's recommended to use the
|
||||
[`fail`](fail.md) shorthand to handle errors.
|
||||
:::
|
||||
|
||||
The type `<R>` of the `output` promise depends on the return type of the `onFulfilled` handler:
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = {...}
|
||||
auto output = input.then([](int res) {
|
||||
return QString::number(res); // -> QPromise<QString>
|
||||
});
|
||||
|
||||
// output type: QPromise<QString>
|
||||
output.then([](const QString& res) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
::: tip NOTE
|
||||
Only `onFulfilled` can change the promise type, `onRejected` **must** return the
|
||||
same type as `onFulfilled`. That also means if `onFulfilled` is `nullptr`,
|
||||
`onRejected` must return the same type as the `input` promise.
|
||||
:::
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = ...
|
||||
auto output = input.then([](int res) {
|
||||
return res + 4;
|
||||
}, [](const ReasonType& reason) {
|
||||
return -1;
|
||||
});
|
||||
```
|
||||
|
||||
If `onFulfilled` doesn't return any value, the `output` type is `QPromise<void>`:
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = ...
|
||||
auto output = input.then([](int res) {
|
||||
// {...}
|
||||
});
|
||||
|
||||
// output type: QPromise<void>
|
||||
output.then([]() {
|
||||
// `QPromise<void>` `onFulfilled` handler has no argument
|
||||
});
|
||||
```
|
||||
|
||||
You can also decide to skip the promise result by omitting the handler argument:
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = {...}
|
||||
auto output = input.then([]( /* skip int result */ ) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
The `output` promise can be *rejected* by throwing an exception in either `onFulfilled` or `onRejected`:
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = {...}
|
||||
auto output = input.then([](int res) {
|
||||
if (res == -1) {
|
||||
throw ReasonType();
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
});
|
||||
|
||||
// output.isRejected() is true
|
||||
```
|
||||
|
||||
If an handler returns a promise (or QFuture), the `output` promise is delayed and will be resolved by the returned promise.
|
24
docs/qtpromise/qpromise/timeout.md
Normal file
24
docs/qtpromise/qpromise/timeout.md
Normal file
@ -0,0 +1,24 @@
|
||||
---
|
||||
title: .timeout
|
||||
---
|
||||
|
||||
# QPromise::timeout
|
||||
|
||||
*Since: 0.2.0*
|
||||
|
||||
```cpp
|
||||
QPromise<T>::timeout(int msec, any error = QPromiseTimeoutException) -> QPromise<T>
|
||||
```
|
||||
|
||||
This method returns a promise that will be resolved with the `input` promise's fulfillment value or rejection reason. However, if the `input` promise is not fulfilled or rejected within `msec` milliseconds, the `output` promise is rejected with `error` as the reason (`QPromiseTimeoutException` by default).
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = {...}
|
||||
auto output = input.timeout(2000)
|
||||
.then([](int res) {
|
||||
// operation succeeded within 2 seconds
|
||||
})
|
||||
.fail([](const QPromiseTimeoutException& e) {
|
||||
// operation timed out!
|
||||
});
|
||||
```
|
29
docs/qtpromise/qpromise/wait.md
Normal file
29
docs/qtpromise/qpromise/wait.md
Normal file
@ -0,0 +1,29 @@
|
||||
---
|
||||
title: .wait
|
||||
---
|
||||
|
||||
# QPromise::wait
|
||||
|
||||
*Since: 0.1.0*
|
||||
|
||||
```cpp
|
||||
QPromise<T>::wait() -> QPromise<T>
|
||||
```
|
||||
|
||||
This method holds the execution of the remaining code until the `input` promise is resolved (either fulfilled or rejected), **without** blocking the event loop of the current thread:
|
||||
|
||||
```cpp
|
||||
int result = -1;
|
||||
|
||||
QPromise<int> input = qPromise(QtConcurrent::run([]() {
|
||||
return 42;
|
||||
})).tap([&](int res) {
|
||||
result = res;
|
||||
});
|
||||
|
||||
// input.isPending() is true && result is -1
|
||||
|
||||
input.wait();
|
||||
|
||||
// input.isPending() is false && result is 42
|
||||
```
|
66
docs/qtpromise/qtconcurrent.md
Normal file
66
docs/qtpromise/qtconcurrent.md
Normal file
@ -0,0 +1,66 @@
|
||||
# QtConcurrent
|
||||
|
||||
QtPromise integrates with [QtConcurrent](https://doc.qt.io/qt-5/qtconcurrent-index.html) to make easy chaining QFuture with QPromise.
|
||||
|
||||
## <a name="qtconcurrent-convert"></a> Convert
|
||||
|
||||
Converting `QFuture<T>` to `QPromise<T>` is done using the [`qPromise`](helpers/qpromise.md) helper:
|
||||
|
||||
```cpp
|
||||
QFuture<int> future = QtConcurrent::run([]() {
|
||||
// {...}
|
||||
return 42;
|
||||
});
|
||||
|
||||
QPromise<int> promise = qPromise(future);
|
||||
```
|
||||
|
||||
or simply:
|
||||
|
||||
```cpp
|
||||
auto promise = qPromise(QtConcurrent::run([]() {
|
||||
// {...}
|
||||
}));
|
||||
```
|
||||
|
||||
## Chain
|
||||
|
||||
Returning a `QFuture<T>` in [`then`](qpromise/then.md) or [`fail`](qpromise/fail.md) automatically translate to `QPromise<T>`:
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = ...
|
||||
auto output = input.then([](int res) {
|
||||
return QtConcurrent::run([]() {
|
||||
// {...}
|
||||
return QString("42");
|
||||
});
|
||||
});
|
||||
|
||||
// output type: QPromise<QString>
|
||||
output.then([](const QString& res) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
The `output` promise is resolved when the `QFuture` is [finished](https://doc.qt.io/qt-5/qfuture.html#isFinished).
|
||||
|
||||
## Error
|
||||
|
||||
Exceptions thrown from a QtConcurrent thread reject the associated promise with the exception as the reason. Note that if you throw an exception that is not a subclass of `QException`, the promise with be rejected with [`QUnhandledException`](https://doc.qt.io/qt-5/qunhandledexception.html#details) (this restriction only applies to exceptions thrown from a QtConcurrent thread, [read more](https://doc.qt.io/qt-5/qexception.html#details)).
|
||||
|
||||
```cpp
|
||||
QPromise<int> promise = ...
|
||||
promise.then([](int res) {
|
||||
return QtConcurrent::run([]() {
|
||||
// {...}
|
||||
|
||||
if (!success) {
|
||||
throw CustomException();
|
||||
}
|
||||
|
||||
return QString("42");
|
||||
});
|
||||
}).fail(const CustomException& err) {
|
||||
// {...}
|
||||
});
|
||||
```
|
7
docs/qtpromise/thread-safety.md
Normal file
7
docs/qtpromise/thread-safety.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Thread-Safety
|
||||
|
||||
QPromise is thread-safe and can be copied and accessed across different threads. QPromise relies on [explicitly data sharing](https://doc.qt.io/qt-5/qexplicitlyshareddatapointer.html#details) and thus `auto p2 = p1` represents the same promise: when `p1` resolves, handlers registered on `p1` and `p2` are called, the fulfilled value being shared between both instances.
|
||||
|
||||
::: warning IMPORTANT
|
||||
While it's safe to access the resolved value from different threads using [`then`](qpromise/then.md), QPromise provides no guarantee about the object being pointed to. Thread-safety and reentrancy rules for that object still apply.
|
||||
:::
|
4
qpm.json
4
qpm.json
@ -10,9 +10,9 @@
|
||||
"url": "https://github.com/simonbrunel/qtpromise.git"
|
||||
},
|
||||
"version": {
|
||||
"label": "0.2.0"
|
||||
"label": "0.4.0"
|
||||
},
|
||||
"license": "MIT",
|
||||
"pri_filename": "qtpromise.pri",
|
||||
"webpage": "https://github.com/simonbrunel/qtpromise"
|
||||
"webpage": "https://qtpromise.netlify.com"
|
||||
}
|
||||
|
@ -3,7 +3,9 @@
|
||||
|
||||
// QtPromise
|
||||
#include "qpromise_p.h"
|
||||
#include "qpromiseerror.h"
|
||||
#include "qpromiseglobal.h"
|
||||
#include "qpromiseresolver.h"
|
||||
|
||||
// Qt
|
||||
#include <QExplicitlySharedDataPointer>
|
||||
@ -16,25 +18,38 @@ class QPromiseBase
|
||||
public:
|
||||
using Type = T;
|
||||
|
||||
QPromiseBase(const QPromiseBase<T>& other): m_d(other.m_d) {}
|
||||
QPromiseBase(const QPromise<T>& other): m_d(other.m_d) {}
|
||||
QPromiseBase(QPromiseBase<T>&& other) { swap(other); }
|
||||
|
||||
template <typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count == 1, int>::type = 0>
|
||||
inline QPromiseBase(F resolver);
|
||||
|
||||
template <typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count != 1, int>::type = 0>
|
||||
inline QPromiseBase(F resolver);
|
||||
|
||||
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); }
|
||||
|
||||
virtual ~QPromiseBase() { }
|
||||
|
||||
QPromiseBase<T>& operator=(const QPromiseBase<T>& other) { m_d = other.m_d; return *this;}
|
||||
QPromiseBase<T>& operator=(QPromiseBase<T>&& other) Q_DECL_NOEXCEPT
|
||||
{ QPromiseBase<T>(std::move(other)).swap(*this); return *this; }
|
||||
|
||||
bool operator==(const QPromiseBase<T>& other) const { return (m_d == other.m_d); }
|
||||
bool operator!=(const QPromiseBase<T>& other) const { return (m_d != other.m_d); }
|
||||
|
||||
void swap(QPromiseBase<T>& other) Q_DECL_NOEXCEPT { qSwap(m_d, other.m_d); }
|
||||
|
||||
bool isFulfilled() const { return m_d->isFulfilled(); }
|
||||
bool isRejected() const { return m_d->isRejected(); }
|
||||
bool isPending() const { return m_d->isPending(); }
|
||||
|
||||
template <typename TFulfilled, typename TRejected = std::nullptr_t>
|
||||
template <typename TFulfilled, typename TRejected>
|
||||
inline typename QtPromisePrivate::PromiseHandler<T, TFulfilled>::Promise
|
||||
then(const TFulfilled& fulfilled, const TRejected& rejected = nullptr) const;
|
||||
then(const TFulfilled& fulfilled, const TRejected& rejected) const;
|
||||
|
||||
template <typename TFulfilled>
|
||||
inline typename QtPromisePrivate::PromiseHandler<T, TFulfilled>::Promise
|
||||
then(TFulfilled&& fulfilled) const;
|
||||
|
||||
template <typename TRejected>
|
||||
inline typename QtPromisePrivate::PromiseHandler<T, std::nullptr_t>::Promise
|
||||
@ -46,35 +61,48 @@ 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;
|
||||
|
||||
inline QPromise<T> delay(int msec) const;
|
||||
inline QPromise<T> wait() const;
|
||||
|
||||
void swap(QPromiseBase<T>& other) { qSwap(m_d, other.m_d); }
|
||||
|
||||
public: // STATIC
|
||||
template <typename E>
|
||||
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>
|
||||
class QPromise: public QPromiseBase<T>
|
||||
class QPromise : public QPromiseBase<T>
|
||||
{
|
||||
public:
|
||||
template <typename F>
|
||||
QPromise(F&& resolver): QPromiseBase<T>(std::forward<F>(resolver)) { }
|
||||
|
||||
template <typename Functor>
|
||||
inline QPromise<T> each(Functor fn);
|
||||
|
||||
template <typename Functor>
|
||||
inline QPromise<T> filter(Functor fn);
|
||||
|
||||
template <typename Functor>
|
||||
inline typename QtPromisePrivate::PromiseMapper<T, Functor>::PromiseType
|
||||
map(Functor fn);
|
||||
|
||||
public: // STATIC
|
||||
inline static QPromise<QVector<T> > all(const QVector<QPromise<T> >& promises);
|
||||
template <template <typename, typename...> class Sequence = QVector, typename ...Args>
|
||||
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);
|
||||
|
||||
private:
|
||||
@ -82,14 +110,16 @@ private:
|
||||
};
|
||||
|
||||
template <>
|
||||
class QPromise<void>: public QPromiseBase<void>
|
||||
class QPromise<void> : public QPromiseBase<void>
|
||||
{
|
||||
public:
|
||||
template <typename F>
|
||||
QPromise(F&& resolver): QPromiseBase<void>(std::forward<F>(resolver)) { }
|
||||
|
||||
public: // STATIC
|
||||
inline static QPromise<void> all(const QVector<QPromise<void> >& promises);
|
||||
template <template <typename, typename...> class Sequence = QVector, typename ...Args>
|
||||
inline static QPromise<void> all(const Sequence<QPromise<void>, Args...>& promises);
|
||||
|
||||
inline static QPromise<void> resolve();
|
||||
|
||||
private:
|
||||
|
@ -1,3 +1,6 @@
|
||||
#include "qpromise.h"
|
||||
#include "qpromisehelpers.h"
|
||||
|
||||
// Qt
|
||||
#include <QCoreApplication>
|
||||
#include <QSharedPointer>
|
||||
@ -5,98 +8,31 @@
|
||||
|
||||
namespace QtPromise {
|
||||
|
||||
template <class T>
|
||||
class QPromiseResolve
|
||||
{
|
||||
public:
|
||||
QPromiseResolve(QPromise<T> p)
|
||||
: m_promise(new QPromise<T>(std::move(p)))
|
||||
{ }
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
QSharedPointer<QPromise<void> > m_promise;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class QPromiseReject
|
||||
{
|
||||
public:
|
||||
QPromiseReject(QPromise<T> p)
|
||||
: m_promise(new QPromise<T>(std::move(p)))
|
||||
{ }
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
QSharedPointer<QPromise<T> > m_promise;
|
||||
};
|
||||
|
||||
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>())
|
||||
{
|
||||
QPromiseResolve<T> resolve(*this);
|
||||
QPromiseReject<T> reject(*this);
|
||||
QtPromisePrivate::PromiseResolver<T> resolver(*this);
|
||||
|
||||
try {
|
||||
resolver(resolve, reject);
|
||||
callback(QPromiseResolve<T>(resolver), QPromiseReject<T>(resolver));
|
||||
} catch (...) {
|
||||
reject(std::current_exception());
|
||||
resolver.reject(std::current_exception());
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,6 +58,14 @@ QPromiseBase<T>::then(const TFulfilled& fulfilled, const TRejected& rejected) co
|
||||
return next;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename TFulfilled>
|
||||
inline typename QtPromisePrivate::PromiseHandler<T, TFulfilled>::Promise
|
||||
QPromiseBase<T>::then(TFulfilled&& fulfilled) const
|
||||
{
|
||||
return then(std::forward<TFulfilled>(fulfilled), nullptr);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename TRejected>
|
||||
inline typename QtPromisePrivate::PromiseHandler<T, std::nullptr_t>::Promise
|
||||
@ -150,6 +94,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
|
||||
@ -166,7 +120,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);
|
||||
});
|
||||
}
|
||||
|
||||
@ -202,22 +156,66 @@ inline QPromise<T> QPromiseBase<T>::reject(E&& error)
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline QPromise<QVector<T> > QPromise<T>::all(const QVector<QPromise<T> >& promises)
|
||||
template <typename Functor>
|
||||
inline QPromise<T> QPromise<T>::each(Functor fn)
|
||||
{
|
||||
const int count = promises.size();
|
||||
return this->tap([=](const T& values) {
|
||||
int i = 0;
|
||||
|
||||
std::vector<QPromise<void>> promises;
|
||||
for (const auto& v : values) {
|
||||
promises.push_back(
|
||||
QtPromise::attempt(fn, v, i)
|
||||
.then([]() {
|
||||
// Cast to void in case fn returns a non promise value.
|
||||
// TODO remove when implicit cast is implemented.
|
||||
}));
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return QPromise<void>::all(promises);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename Functor>
|
||||
inline QPromise<T> QPromise<T>::filter(Functor fn)
|
||||
{
|
||||
return this->then([=](const T& values) {
|
||||
return QtPromise::filter(values, fn);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename Functor>
|
||||
inline typename QtPromisePrivate::PromiseMapper<T, Functor>::PromiseType
|
||||
QPromise<T>::map(Functor fn)
|
||||
{
|
||||
return this->then([=](const T& values) {
|
||||
return QtPromise::map(values, fn);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <template <typename, typename...> class Sequence, typename ...Args>
|
||||
inline QPromise<QVector<T>> QPromise<T>::all(const Sequence<QPromise<T>, Args...>& promises)
|
||||
{
|
||||
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));
|
||||
|
||||
for (int i=0; i<count; ++i) {
|
||||
promises[i].then([=](const T& res) mutable {
|
||||
int i = 0;
|
||||
for (const auto& promise: promises) {
|
||||
promise.then([=](const T& res) mutable {
|
||||
(*results)[i] = res;
|
||||
if (--(*remaining) == 0) {
|
||||
resolve(*results);
|
||||
@ -228,10 +226,20 @@ inline QPromise<QVector<T> > QPromise<T>::all(const QVector<QPromise<T> >& promi
|
||||
reject(std::current_exception());
|
||||
}
|
||||
});
|
||||
|
||||
i++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline QPromise<T> QPromise<T>::resolve(const T& value)
|
||||
{
|
||||
return QPromise<T>([&](const QPromiseResolve<T>& resolve) {
|
||||
resolve(value);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline QPromise<T> QPromise<T>::resolve(T&& value)
|
||||
{
|
||||
@ -240,9 +248,10 @@ inline QPromise<T> QPromise<T>::resolve(T&& value)
|
||||
});
|
||||
}
|
||||
|
||||
inline QPromise<void> QPromise<void>::all(const QVector<QPromise<void> >& promises)
|
||||
template <template <typename, typename...> class Sequence, typename ...Args>
|
||||
inline QPromise<void> QPromise<void>::all(const Sequence<QPromise<void>, Args...>& promises)
|
||||
{
|
||||
const int count = promises.size();
|
||||
const int count = static_cast<int>(promises.size());
|
||||
if (count == 0) {
|
||||
return QPromise<void>::resolve();
|
||||
}
|
||||
@ -251,7 +260,7 @@ inline QPromise<void> QPromise<void>::all(const QVector<QPromise<void> >& promis
|
||||
const QPromiseResolve<void>& resolve,
|
||||
const QPromiseReject<void>& reject) {
|
||||
|
||||
QSharedPointer<int> remaining(new int(promises.size()));
|
||||
QSharedPointer<int> remaining(new int(count));
|
||||
|
||||
for (const auto& promise: promises) {
|
||||
promise.then([=]() {
|
||||
|
@ -2,7 +2,6 @@
|
||||
#define QTPROMISE_QPROMISE_P_H
|
||||
|
||||
// QtPromise
|
||||
#include "qpromiseerror.h"
|
||||
#include "qpromiseglobal.h"
|
||||
|
||||
// Qt
|
||||
@ -32,33 +31,101 @@ namespace QtPromisePrivate {
|
||||
|
||||
// https://stackoverflow.com/a/21653558
|
||||
template <typename F>
|
||||
static void qtpromise_defer(F&& f, QThread* thread = nullptr)
|
||||
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(); }
|
||||
FType m_f;
|
||||
};
|
||||
|
||||
if (!thread || thread->isFinished()) {
|
||||
// Make sure to not call `f` if the captured thread doesn't exist anymore,
|
||||
// which would potentially result in dispatching to the wrong thread (ie.
|
||||
// nullptr == current thread). Since the target thread is gone, it should
|
||||
// be safe to simply skip that notification.
|
||||
return;
|
||||
}
|
||||
|
||||
QObject* target = QAbstractEventDispatcher::instance(thread);
|
||||
if (!target && QCoreApplication::closingDown()) {
|
||||
// When the app is shutting down, the even loop is not anymore available
|
||||
// so we don't have any way to dispatch `f`. This case can happen when a
|
||||
// promise is resolved after the app is requested to close, in which case
|
||||
// we should not trigger any error and skip that notification.
|
||||
return;
|
||||
}
|
||||
|
||||
Q_ASSERT_X(target, "postMetaCall", "Target thread must have an event loop");
|
||||
QCoreApplication::postEvent(target, new Event(std::forward<F>(f)));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct PromiseDeduce
|
||||
template <typename F>
|
||||
static void qtpromise_defer(F&& f)
|
||||
{
|
||||
using Type = QtPromise::QPromise<Unqualified<T> >;
|
||||
Q_ASSERT(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>
|
||||
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 Functor, typename... Args>
|
||||
struct PromiseFunctor
|
||||
{
|
||||
using ResultType = typename std::result_of<Functor(Args...)>::type;
|
||||
using PromiseType = typename PromiseDeduce<ResultType>::Type;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct PromiseFulfill
|
||||
{
|
||||
@ -72,7 +139,7 @@ struct PromiseFulfill
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct PromiseFulfill<QtPromise::QPromise<T> >
|
||||
struct PromiseFulfill<QtPromise::QPromise<T>>
|
||||
{
|
||||
static void call(
|
||||
const QtPromise::QPromise<T>& promise,
|
||||
@ -94,7 +161,7 @@ struct PromiseFulfill<QtPromise::QPromise<T> >
|
||||
};
|
||||
|
||||
template <>
|
||||
struct PromiseFulfill<QtPromise::QPromise<void> >
|
||||
struct PromiseFulfill<QtPromise::QPromise<void>>
|
||||
{
|
||||
template <typename TPromise, typename TResolve, typename TReject>
|
||||
static void call(
|
||||
@ -116,51 +183,17 @@ struct PromiseFulfill<QtPromise::QPromise<void> >
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename TRes>
|
||||
template <typename Result>
|
||||
struct PromiseDispatch
|
||||
{
|
||||
using Promise = typename PromiseDeduce<TRes>::Type;
|
||||
using ResType = Unqualified<TRes>;
|
||||
|
||||
template <typename THandler, typename TResolve, typename TReject>
|
||||
static void call(const T& value, THandler handler, const TResolve& resolve, const TReject& reject)
|
||||
template <typename Resolve, typename Reject, typename Functor, typename... Args>
|
||||
static void call(const Resolve& resolve, const Reject& reject, Functor fn, Args&&... args)
|
||||
{
|
||||
try {
|
||||
PromiseFulfill<ResType>::call(handler(value), resolve, reject);
|
||||
} catch (...) {
|
||||
reject(std::current_exception());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct PromiseDispatch<T, void>
|
||||
{
|
||||
using Promise = QtPromise::QPromise<void>;
|
||||
|
||||
template <typename THandler, typename TResolve, typename TReject>
|
||||
static void call(const T& value, THandler handler, const TResolve& resolve, const TReject& reject)
|
||||
{
|
||||
try {
|
||||
handler(value);
|
||||
resolve();
|
||||
} catch (...) {
|
||||
reject(std::current_exception());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TRes>
|
||||
struct PromiseDispatch<void, TRes>
|
||||
{
|
||||
using Promise = typename PromiseDeduce<TRes>::Type;
|
||||
using ResType = Unqualified<TRes>;
|
||||
|
||||
template <typename THandler, typename TResolve, typename TReject>
|
||||
static void call(THandler handler, const TResolve& resolve, const TReject& reject)
|
||||
{
|
||||
try {
|
||||
PromiseFulfill<ResType>::call(handler(), resolve, reject);
|
||||
PromiseFulfill<Unqualified<Result>>::call(
|
||||
fn(std::forward<Args>(args)...),
|
||||
resolve,
|
||||
reject);
|
||||
} catch (...) {
|
||||
reject(std::current_exception());
|
||||
}
|
||||
@ -168,15 +201,13 @@ struct PromiseDispatch<void, TRes>
|
||||
};
|
||||
|
||||
template <>
|
||||
struct PromiseDispatch<void, void>
|
||||
struct PromiseDispatch<void>
|
||||
{
|
||||
using Promise = QtPromise::QPromise<void>;
|
||||
|
||||
template <typename THandler, typename TResolve, typename TReject>
|
||||
static void call(THandler handler, const TResolve& resolve, const TReject& reject)
|
||||
template <typename Resolve, typename Reject, typename Functor, typename... Args>
|
||||
static void call(const Resolve& resolve, const Reject& reject, Functor fn, Args&&... args)
|
||||
{
|
||||
try {
|
||||
handler();
|
||||
fn(std::forward<Args>(args)...);
|
||||
resolve();
|
||||
} catch (...) {
|
||||
reject(std::current_exception());
|
||||
@ -188,7 +219,7 @@ template <typename T, typename THandler, typename TArg = typename ArgsOf<THandle
|
||||
struct PromiseHandler
|
||||
{
|
||||
using ResType = typename std::result_of<THandler(T)>::type;
|
||||
using Promise = typename PromiseDispatch<T, ResType>::Promise;
|
||||
using Promise = typename PromiseDeduce<ResType>::Type;
|
||||
|
||||
template <typename TResolve, typename TReject>
|
||||
static std::function<void(const T&)> create(
|
||||
@ -197,7 +228,7 @@ struct PromiseHandler
|
||||
const TReject& reject)
|
||||
{
|
||||
return [=](const T& value) {
|
||||
PromiseDispatch<T, ResType>::call(value, std::move(handler), resolve, reject);
|
||||
PromiseDispatch<ResType>::call(resolve, reject, handler, value);
|
||||
};
|
||||
}
|
||||
};
|
||||
@ -206,7 +237,7 @@ template <typename T, typename THandler>
|
||||
struct PromiseHandler<T, THandler, void>
|
||||
{
|
||||
using ResType = typename std::result_of<THandler()>::type;
|
||||
using Promise = typename PromiseDispatch<T, ResType>::Promise;
|
||||
using Promise = typename PromiseDeduce<ResType>::Type;
|
||||
|
||||
template <typename TResolve, typename TReject>
|
||||
static std::function<void(const T&)> create(
|
||||
@ -215,7 +246,7 @@ struct PromiseHandler<T, THandler, void>
|
||||
const TReject& reject)
|
||||
{
|
||||
return [=](const T&) {
|
||||
PromiseDispatch<void, ResType>::call(handler, resolve, reject);
|
||||
PromiseDispatch<ResType>::call(resolve, reject, handler);
|
||||
};
|
||||
}
|
||||
};
|
||||
@ -224,7 +255,7 @@ template <typename THandler>
|
||||
struct PromiseHandler<void, THandler, void>
|
||||
{
|
||||
using ResType = typename std::result_of<THandler()>::type;
|
||||
using Promise = typename PromiseDispatch<void, ResType>::Promise;
|
||||
using Promise = typename PromiseDeduce<ResType>::Type;
|
||||
|
||||
template <typename TResolve, typename TReject>
|
||||
static std::function<void()> create(
|
||||
@ -233,7 +264,7 @@ struct PromiseHandler<void, THandler, void>
|
||||
const TReject& reject)
|
||||
{
|
||||
return [=]() {
|
||||
PromiseDispatch<void, ResType>::call(handler, resolve, reject);
|
||||
PromiseDispatch<ResType>::call(resolve, reject, handler);
|
||||
};
|
||||
}
|
||||
};
|
||||
@ -282,16 +313,16 @@ 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) {
|
||||
PromiseDispatch<TArg, ResType>::call(error, handler, resolve, reject);
|
||||
PromiseDispatch<ResType>::call(resolve, reject, handler, error);
|
||||
} catch (...) {
|
||||
reject(std::current_exception());
|
||||
}
|
||||
@ -305,16 +336,16 @@ 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 (...) {
|
||||
PromiseDispatch<void, ResType>::call(handler, resolve, reject);
|
||||
PromiseDispatch<ResType>::call(resolve, reject, handler);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -324,12 +355,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);
|
||||
@ -337,15 +368,26 @@ struct PromiseCatcher<T, std::nullptr_t, void>
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename F>
|
||||
struct PromiseMapper
|
||||
{ };
|
||||
|
||||
template <typename T, typename F, template <typename, typename...> class Sequence, typename ...Args>
|
||||
struct PromiseMapper<Sequence<T, Args...>, F>
|
||||
{
|
||||
using ReturnType = typename std::result_of<F(T, int)>::type;
|
||||
using ResultType = QVector<typename PromiseDeduce<ReturnType>::Type::Type>;
|
||||
using PromiseType = QtPromise::QPromise<ResultType>;
|
||||
};
|
||||
|
||||
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() {}
|
||||
|
||||
@ -371,29 +413,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;
|
||||
@ -422,13 +457,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);
|
||||
}
|
||||
}
|
||||
@ -449,7 +484,7 @@ private:
|
||||
bool m_settled = false;
|
||||
QVector<Handler> m_handlers;
|
||||
QVector<Catcher> m_catchers;
|
||||
QSharedPointer<Error> m_error;
|
||||
PromiseError m_error;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
@ -458,31 +493,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;
|
||||
@ -490,25 +510,25 @@ 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 <>
|
||||
class PromiseData<void> : public PromiseDataBase<void, void()>
|
||||
{
|
||||
using Handler = typename PromiseDataBase<void, void()>::Handler;
|
||||
using Handler = PromiseDataBase<void, void()>::Handler;
|
||||
|
||||
public:
|
||||
void resolve()
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -16,18 +16,18 @@ using Unqualified = typename std::remove_cv<typename std::remove_reference<T>::t
|
||||
|
||||
/*!
|
||||
* \struct HasCallOperator
|
||||
* http://stackoverflow.com/a/5839442
|
||||
* http://stackoverflow.com/a/5117641
|
||||
*/
|
||||
template <typename T>
|
||||
struct HasCallOperator
|
||||
{
|
||||
template <class U>
|
||||
static auto check(const U* u)
|
||||
-> decltype(&U::operator(), char(0));
|
||||
template <typename U>
|
||||
static char check(decltype(&U::operator(), char(0)));
|
||||
|
||||
static std::array<char, 2> check(...);
|
||||
template <typename U>
|
||||
static char (&check(...))[2];
|
||||
|
||||
static const bool value = (sizeof(check((T*)0)) == 1);
|
||||
static const bool value = (sizeof(check<T>(0)) == 1);
|
||||
};
|
||||
|
||||
/*!
|
||||
@ -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>
|
||||
|
@ -26,17 +26,95 @@ static inline QPromise<void> qPromise()
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline QPromise<QVector<T> > qPromiseAll(const QVector<QPromise<T> >& promises)
|
||||
template <typename T, template <typename, typename...> class Sequence = QVector, typename ...Args>
|
||||
static inline QPromise<QVector<T>> qPromiseAll(const Sequence<QPromise<T>, Args...>& promises)
|
||||
{
|
||||
return QPromise<T>::all(promises);
|
||||
}
|
||||
|
||||
static inline QPromise<void> qPromiseAll(const QVector<QPromise<void> >& promises)
|
||||
template <template <typename, typename...> class Sequence = QVector, typename ...Args>
|
||||
static inline QPromise<void> qPromiseAll(const Sequence<QPromise<void>, Args...>& promises)
|
||||
{
|
||||
return QPromise<void>::all(promises);
|
||||
}
|
||||
|
||||
template <typename Functor, typename... Args>
|
||||
static inline typename QtPromisePrivate::PromiseFunctor<Functor, Args...>::PromiseType
|
||||
attempt(Functor&& fn, Args&&... args)
|
||||
{
|
||||
using namespace QtPromisePrivate;
|
||||
using FunctorType = PromiseFunctor<Functor, Args...>;
|
||||
using PromiseType = typename FunctorType::PromiseType;
|
||||
using ValueType = typename PromiseType::Type;
|
||||
|
||||
// NOTE: std::forward<T<U>>: MSVC 2013 fails when forwarding
|
||||
// template type (error: "expects 4 arguments - 0 provided").
|
||||
// However it succeeds with type alias.
|
||||
// TODO: should we expose QPromise::ResolveType & RejectType?
|
||||
using ResolveType = QPromiseResolve<ValueType>;
|
||||
using RejectType = QPromiseReject<ValueType>;
|
||||
|
||||
return PromiseType(
|
||||
[&](ResolveType&& resolve, RejectType&& reject) {
|
||||
PromiseDispatch<typename FunctorType::ResultType>::call(
|
||||
std::forward<ResolveType>(resolve),
|
||||
std::forward<RejectType>(reject),
|
||||
std::forward<Functor>(fn),
|
||||
std::forward<Args>(args)...);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Sequence, typename Functor>
|
||||
static inline QPromise<Sequence> each(const Sequence& values, Functor&& fn)
|
||||
{
|
||||
return QPromise<Sequence>::resolve(values).each(std::forward<Functor>(fn));
|
||||
}
|
||||
|
||||
template <typename Sequence, typename Functor>
|
||||
static inline typename QtPromisePrivate::PromiseMapper<Sequence, Functor>::PromiseType
|
||||
map(const Sequence& values, Functor fn)
|
||||
{
|
||||
using namespace QtPromisePrivate;
|
||||
using MapperType = PromiseMapper<Sequence, Functor>;
|
||||
using ResType = typename MapperType::ResultType::value_type;
|
||||
using RetType = typename MapperType::ReturnType;
|
||||
|
||||
int i = 0;
|
||||
|
||||
std::vector<QPromise<ResType>> promises;
|
||||
for (const auto& v : values) {
|
||||
promises.push_back(QPromise<ResType>([&](
|
||||
const QPromiseResolve<ResType>& resolve,
|
||||
const QPromiseReject<ResType>& reject) {
|
||||
PromiseFulfill<RetType>::call(fn(v, i), resolve, reject);
|
||||
}));
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return QPromise<ResType>::all(promises);
|
||||
}
|
||||
|
||||
template <typename Sequence, typename Functor>
|
||||
static inline QPromise<Sequence> filter(const Sequence& values, Functor fn)
|
||||
{
|
||||
return QtPromise::map(values, fn)
|
||||
.then([=](const QVector<bool>& filters) {
|
||||
Sequence filtered;
|
||||
|
||||
auto filter = filters.begin();
|
||||
for (auto& value : values) {
|
||||
if (*filter) {
|
||||
filtered.push_back(std::move(value));
|
||||
}
|
||||
|
||||
filter++;
|
||||
}
|
||||
|
||||
return filtered;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace QtPromise
|
||||
|
||||
#endif // QTPROMISE_QPROMISEHELPERS_H
|
||||
|
124
src/qtpromise/qpromiseresolver.h
Normal file
124
src/qtpromise/qpromiseresolver.h
Normal file
@ -0,0 +1,124 @@
|
||||
#ifndef QTPROMISE_QPROMISERESOLVER_H
|
||||
#define QTPROMISE_QPROMISERESOLVER_H
|
||||
|
||||
// Qt
|
||||
#include <QExplicitlySharedDataPointer>
|
||||
|
||||
namespace QtPromise {
|
||||
|
||||
template <typename T> class QPromise;
|
||||
|
||||
} // namespace QtPromise
|
||||
|
||||
namespace QtPromisePrivate {
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
} // QtPromisePrivate
|
||||
|
||||
namespace QtPromise {
|
||||
|
||||
template <class T>
|
||||
class QPromiseResolve
|
||||
{
|
||||
public:
|
||||
QPromiseResolve(QtPromisePrivate::PromiseResolver<T> resolver)
|
||||
: m_resolver(std::move(resolver))
|
||||
{ }
|
||||
|
||||
template <typename V>
|
||||
void operator()(V&& value) const
|
||||
{
|
||||
m_resolver.resolve(std::forward<V>(value));
|
||||
}
|
||||
|
||||
void operator()() const
|
||||
{
|
||||
m_resolver.resolve();
|
||||
}
|
||||
|
||||
private:
|
||||
mutable QtPromisePrivate::PromiseResolver<T> m_resolver;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class QPromiseReject
|
||||
{
|
||||
public:
|
||||
QPromiseReject(QtPromisePrivate::PromiseResolver<T> resolver)
|
||||
: m_resolver(std::move(resolver))
|
||||
{ }
|
||||
|
||||
template <typename E>
|
||||
void operator()(E&& error) const
|
||||
{
|
||||
m_resolver.reject(std::forward<E>(error));
|
||||
}
|
||||
|
||||
private:
|
||||
mutable QtPromisePrivate::PromiseResolver<T> m_resolver;
|
||||
};
|
||||
|
||||
} // namespace QtPromise
|
||||
|
||||
#endif // QTPROMISE_QPROMISERESOLVER_H
|
@ -5,4 +5,5 @@ HEADERS += \
|
||||
$$PWD/qpromiseerror.h \
|
||||
$$PWD/qpromisefuture.h \
|
||||
$$PWD/qpromiseglobal.h \
|
||||
$$PWD/qpromisehelpers.h
|
||||
$$PWD/qpromisehelpers.h \
|
||||
$$PWD/qpromiseresolver.h
|
||||
|
@ -1,8 +1,2 @@
|
||||
TEMPLATE = subdirs
|
||||
SUBDIRS += \
|
||||
benchmark \
|
||||
future \
|
||||
helpers \
|
||||
qpromise \
|
||||
requirements \
|
||||
thread
|
||||
SUBDIRS += qtpromise
|
||||
|
@ -1,4 +0,0 @@
|
||||
TARGET = tst_helpers
|
||||
SOURCES += $$PWD/tst_helpers.cpp
|
||||
|
||||
include(../tests.pri)
|
@ -1,244 +0,0 @@
|
||||
// QtPromise
|
||||
#include <QtPromise>
|
||||
|
||||
// Qt
|
||||
#include <QtTest>
|
||||
|
||||
using namespace QtPromise;
|
||||
|
||||
class tst_helpers : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void resolve();
|
||||
void resolve_void();
|
||||
void resolve_promise();
|
||||
void resolve_promise_void();
|
||||
|
||||
void allFulfilled();
|
||||
void allFulfilled_void();
|
||||
void allRejected();
|
||||
void allRejected_void();
|
||||
void allEmpty();
|
||||
void allEmpty_void();
|
||||
|
||||
}; // class tst_helpers
|
||||
|
||||
QTEST_MAIN(tst_helpers)
|
||||
#include "tst_helpers.moc"
|
||||
|
||||
void tst_helpers::resolve()
|
||||
{
|
||||
int value = -1;
|
||||
auto p = QtPromise::qPromise(42);
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int> >::value));
|
||||
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
|
||||
p.then([&](int res) {
|
||||
value = res;
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(value, 42);
|
||||
}
|
||||
|
||||
void tst_helpers::resolve_void()
|
||||
{
|
||||
int value = -1;
|
||||
auto p = QtPromise::qPromise();
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void> >::value));
|
||||
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
|
||||
p.then([&]() {
|
||||
value = 42;
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(value, 42);
|
||||
}
|
||||
|
||||
void tst_helpers::resolve_promise()
|
||||
{
|
||||
QString value;
|
||||
auto p = QtPromise::qPromise(
|
||||
QPromise<QString>([](const QPromiseResolve<QString>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=](){
|
||||
resolve("foo");
|
||||
});
|
||||
}));
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QString> >::value));
|
||||
|
||||
QCOMPARE(p.isPending(), true);
|
||||
|
||||
p.then([&](const QString& res) {
|
||||
value = res;
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(value, QString("foo"));
|
||||
}
|
||||
|
||||
void tst_helpers::resolve_promise_void()
|
||||
{
|
||||
QList<int> values;
|
||||
auto p = QtPromise::qPromise(
|
||||
QPromise<void>([&](const QPromiseResolve<void>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=, &values](){
|
||||
values << 42;
|
||||
resolve();
|
||||
});
|
||||
}));
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void> >::value));
|
||||
|
||||
QCOMPARE(p.isPending(), true);
|
||||
|
||||
p.then([&]() {
|
||||
values << 43;
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(values, QList<int>({42, 43}));
|
||||
}
|
||||
|
||||
void tst_helpers::allFulfilled()
|
||||
{
|
||||
auto p0 = QtPromise::qPromise(42);
|
||||
auto p1 = QtPromise::qPromise(44);
|
||||
auto p2 = QPromise<int>([](const QPromiseResolve<int>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=](){
|
||||
resolve(43);
|
||||
});
|
||||
});
|
||||
|
||||
auto p = qPromiseAll(QVector<QPromise<int> >{p0, p2, p1});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int> > >::value));
|
||||
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(p0.isFulfilled(), true);
|
||||
QCOMPARE(p1.isFulfilled(), true);
|
||||
QCOMPARE(p2.isPending(), true);
|
||||
|
||||
QVector<int> values;
|
||||
p.then([&](const QVector<int>& res) {
|
||||
values = res;
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(p2.isFulfilled(), true);
|
||||
QCOMPARE(values, QVector<int>({42, 43, 44}));
|
||||
}
|
||||
|
||||
void tst_helpers::allFulfilled_void()
|
||||
{
|
||||
auto p0 = QtPromise::qPromise();
|
||||
auto p1 = QtPromise::qPromise();
|
||||
auto p2 = QPromise<void>([](const QPromiseResolve<void>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=](){
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
auto p = qPromiseAll(QVector<QPromise<void> >{p0, p2, p1});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void> >::value));
|
||||
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(p0.isFulfilled(), true);
|
||||
QCOMPARE(p1.isFulfilled(), true);
|
||||
QCOMPARE(p2.isPending(), true);
|
||||
|
||||
p.wait();
|
||||
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(p2.isFulfilled(), true);
|
||||
}
|
||||
|
||||
void tst_helpers::allRejected()
|
||||
{
|
||||
auto p0 = QtPromise::qPromise(42);
|
||||
auto p1 = QtPromise::qPromise(44);
|
||||
auto p2 = QPromise<int>([](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
|
||||
QtPromisePrivate::qtpromise_defer([=](){
|
||||
reject(QString("foo"));
|
||||
});
|
||||
});
|
||||
|
||||
auto p = qPromiseAll(QVector<QPromise<int> >{p0, p2, p1});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int> > >::value));
|
||||
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(p0.isFulfilled(), true);
|
||||
QCOMPARE(p1.isFulfilled(), true);
|
||||
QCOMPARE(p2.isPending(), true);
|
||||
|
||||
QString error;
|
||||
p.fail([&](const QString& err) {
|
||||
error = err;
|
||||
return QVector<int>();
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(p2.isRejected(), true);
|
||||
QCOMPARE(error, QString("foo"));
|
||||
}
|
||||
|
||||
void tst_helpers::allRejected_void()
|
||||
{
|
||||
auto p0 = QtPromise::qPromise();
|
||||
auto p1 = QtPromise::qPromise();
|
||||
auto p2 = QPromise<void>([](const QPromiseResolve<void>&, const QPromiseReject<void>& reject) {
|
||||
QtPromisePrivate::qtpromise_defer([=](){
|
||||
reject(QString("foo"));
|
||||
});
|
||||
});
|
||||
|
||||
auto p = qPromiseAll(QVector<QPromise<void> >{p0, p2, p1});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void> >::value));
|
||||
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(p0.isFulfilled(), true);
|
||||
QCOMPARE(p1.isFulfilled(), true);
|
||||
QCOMPARE(p2.isPending(), true);
|
||||
|
||||
QString error;
|
||||
p.fail([&](const QString& err) {
|
||||
error = err;
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(p2.isRejected(), true);
|
||||
QCOMPARE(error, QString("foo"));
|
||||
}
|
||||
|
||||
void tst_helpers::allEmpty()
|
||||
{
|
||||
auto p = qPromiseAll(QVector<QPromise<int> >());
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int> > >::value));
|
||||
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
|
||||
QVector<int> values;
|
||||
p.then([&](const QVector<int>& res) {
|
||||
values = res;
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(values, QVector<int>());
|
||||
}
|
||||
|
||||
void tst_helpers::allEmpty_void()
|
||||
{
|
||||
auto p = qPromiseAll(QVector<QPromise<void> >());
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void> >::value));
|
||||
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
TARGET = tst_qpromise
|
||||
SOURCES += $$PWD/tst_qpromise.cpp
|
||||
|
||||
include(../tests.pri)
|
@ -1,766 +0,0 @@
|
||||
// QtPromise
|
||||
#include <QtPromise>
|
||||
|
||||
// Qt
|
||||
#include <QtTest>
|
||||
#include <QElapsedTimer>
|
||||
|
||||
using namespace QtPromise;
|
||||
using namespace QtPromisePrivate;
|
||||
|
||||
class tst_qpromise : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void resolveSync();
|
||||
void resolveSync_void();
|
||||
void resolveDelayed();
|
||||
void rejectSync();
|
||||
void rejectDelayed();
|
||||
void rejectThrows();
|
||||
|
||||
void thenReturns();
|
||||
void thenThrows();
|
||||
void thenNullPtr();
|
||||
void thenSkipResult();
|
||||
void thenDelayedResolved();
|
||||
void thenDelayedRejected();
|
||||
|
||||
void failSameType();
|
||||
void failBaseClass();
|
||||
void failCatchAll();
|
||||
|
||||
void finallyFulfilled();
|
||||
void finallyFulfilled_void();
|
||||
void finallyRejected();
|
||||
void finallyRejected_void();
|
||||
void finallyThrows();
|
||||
void finallyThrows_void();
|
||||
void finallyDelayedResolved();
|
||||
void finallyDelayedRejected();
|
||||
|
||||
void tapFulfilled();
|
||||
void tapFulfilled_void();
|
||||
void tapRejected();
|
||||
void tapRejected_void();
|
||||
void tapThrows();
|
||||
void tapThrows_void();
|
||||
void tapDelayedResolved();
|
||||
void tapDelayedRejected();
|
||||
|
||||
void timeoutFulfilled();
|
||||
void timeoutRejected();
|
||||
void timeoutReject();
|
||||
|
||||
void delayFulfilled();
|
||||
void delayRejected();
|
||||
|
||||
}; // class tst_qpromise
|
||||
|
||||
QTEST_MAIN(tst_qpromise)
|
||||
#include "tst_qpromise.moc"
|
||||
|
||||
template <typename T>
|
||||
T waitForValue(const QPromise<T>& promise, const T& initial)
|
||||
{
|
||||
T value(initial);
|
||||
promise.then([&](const T& res) {
|
||||
value = res;
|
||||
}).wait();
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T waitForValue(const QPromise<void>& promise, const T& initial, const T& expected)
|
||||
{
|
||||
T value(initial);
|
||||
promise.then([&]() {
|
||||
value = expected;
|
||||
}).wait();
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename T, typename E>
|
||||
E waitForError(const QPromise<T>& promise, const E& initial)
|
||||
{
|
||||
E error(initial);
|
||||
promise.fail([&](const E& err) {
|
||||
error = err;
|
||||
return T();
|
||||
}).wait();
|
||||
return error;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
E waitForError(const QPromise<void>& promise, const E& initial)
|
||||
{
|
||||
E error(initial);
|
||||
promise.fail([&](const E& err) {
|
||||
error = err;
|
||||
}).wait();
|
||||
return error;
|
||||
}
|
||||
|
||||
void tst_qpromise::resolveSync()
|
||||
{
|
||||
{ // resolver(resolve)
|
||||
QPromise<int> p([](const QPromiseResolve<int>& resolve) {
|
||||
resolve(42);
|
||||
});
|
||||
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(waitForError(p, QString()), QString());
|
||||
QCOMPARE(waitForValue(p, -1), 42);
|
||||
}
|
||||
{ // resolver(resolve, reject)
|
||||
QPromise<int> p([](const QPromiseResolve<int>& resolve, const QPromiseReject<int>&) {
|
||||
resolve(42);
|
||||
});
|
||||
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(waitForError(p, QString()), QString());
|
||||
QCOMPARE(waitForValue(p, -1), 42);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_qpromise::resolveSync_void()
|
||||
{
|
||||
{ // resolver(resolve)
|
||||
QPromise<void> p([](const QPromiseResolve<void>& resolve) {
|
||||
resolve();
|
||||
});
|
||||
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(waitForError(p, QString()), QString());
|
||||
QCOMPARE(waitForValue(p, -1, 42), 42);
|
||||
}
|
||||
{ // resolver(resolve, reject)
|
||||
QPromise<void> p([](const QPromiseResolve<void>& resolve, const QPromiseReject<void>&) {
|
||||
resolve();
|
||||
});
|
||||
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(waitForError(p, QString()), QString());
|
||||
QCOMPARE(waitForValue(p, -1, 42), 42);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_qpromise::resolveDelayed()
|
||||
{
|
||||
{ // resolver(resolve)
|
||||
QPromise<int> p([](const QPromiseResolve<int>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
resolve(42);
|
||||
});
|
||||
});
|
||||
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForError(p, QString()), QString());
|
||||
QCOMPARE(waitForValue(p, -1), 42);
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
}
|
||||
{ // resolver(resolve, reject)
|
||||
QPromise<int> p([](const QPromiseResolve<int>& resolve, const QPromiseReject<int>&) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
resolve(42);
|
||||
});
|
||||
});
|
||||
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForError(p, QString()), QString());
|
||||
QCOMPARE(waitForValue(p, -1), 42);
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_qpromise::rejectSync()
|
||||
{
|
||||
QPromise<int> p([](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
|
||||
reject(QString("foo"));
|
||||
});
|
||||
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(waitForValue(p, -1), -1);
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
}
|
||||
|
||||
void tst_qpromise::rejectDelayed()
|
||||
{
|
||||
QPromise<int> p([](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
reject(QString("foo"));
|
||||
});
|
||||
});
|
||||
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, -1), -1);
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
}
|
||||
|
||||
void tst_qpromise::rejectThrows()
|
||||
{
|
||||
{ // resolver(resolve)
|
||||
QPromise<int> p([](const QPromiseResolve<int>&) {
|
||||
throw QString("foo");
|
||||
});
|
||||
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(waitForValue(p, -1), -1);
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
}
|
||||
{ // resolver(resolve, reject)
|
||||
QPromise<int> p([](const QPromiseResolve<int>&, const QPromiseReject<int>&) {
|
||||
throw QString("foo");
|
||||
});
|
||||
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(waitForValue(p, -1), -1);
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
}
|
||||
}
|
||||
|
||||
void tst_qpromise::thenReturns()
|
||||
{
|
||||
auto p = QPromise<int>::resolve(42);
|
||||
|
||||
QVariantList values;
|
||||
p.then([&](int res) {
|
||||
values << res;
|
||||
return QString::number(res+1);
|
||||
}).then([&](const QString& res) {
|
||||
values << res;
|
||||
}).then([&]() {
|
||||
values << 44;
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(values, QVariantList({42, QString("43"), 44}));
|
||||
}
|
||||
|
||||
void tst_qpromise::thenThrows()
|
||||
{
|
||||
auto input = QPromise<int>::resolve(42);
|
||||
auto output = input.then([](int res) {
|
||||
throw QString("foo%1").arg(res);
|
||||
return 42;
|
||||
});
|
||||
|
||||
QString error;
|
||||
output.then([&](int res) {
|
||||
error += "bar" + QString::number(res);
|
||||
}).fail([&](const QString& err) {
|
||||
error += err;
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(input.isFulfilled(), true);
|
||||
QCOMPARE(output.isRejected(), true);
|
||||
QCOMPARE(error, QString("foo42"));
|
||||
}
|
||||
|
||||
void tst_qpromise::thenNullPtr()
|
||||
{
|
||||
{ // resolved
|
||||
auto p = QPromise<int>::resolve(42).then(nullptr);
|
||||
|
||||
QCOMPARE(waitForValue(p, -1), 42);
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
}
|
||||
{ // rejected
|
||||
auto p = QPromise<int>::reject(QString("foo")).then(nullptr);
|
||||
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_qpromise::thenSkipResult()
|
||||
{
|
||||
auto p = QPromise<int>::resolve(42);
|
||||
|
||||
int value = -1;
|
||||
p.then([&]() {
|
||||
value = 43;
|
||||
}).wait();
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int> >::value));
|
||||
QCOMPARE(value, 43);
|
||||
}
|
||||
|
||||
void tst_qpromise::thenDelayedResolved()
|
||||
{
|
||||
auto p = QPromise<int>::resolve(42).then([](int res) {
|
||||
return QPromise<QString>([=](const QPromiseResolve<QString>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
resolve(QString("foo%1").arg(res));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QString> >::value));
|
||||
QCOMPARE(waitForValue(p, QString()), QString("foo42"));
|
||||
}
|
||||
|
||||
void tst_qpromise::thenDelayedRejected()
|
||||
{
|
||||
auto p = QPromise<int>::resolve(42).then([](int res) {
|
||||
return QPromise<void>([=](const QPromiseResolve<void>&, const QPromiseReject<void>& reject) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
reject(QString("foo%1").arg(res));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void> >::value));
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo42"));
|
||||
}
|
||||
|
||||
void tst_qpromise::failSameType()
|
||||
{
|
||||
// http://en.cppreference.com/w/cpp/error/exception
|
||||
auto p = QPromise<int>::reject(std::out_of_range("foo"));
|
||||
|
||||
QString error;
|
||||
p.fail([&](const std::domain_error& e) {
|
||||
error += QString(e.what()) + "0";
|
||||
return -1;
|
||||
}).fail([&](const std::out_of_range& e) {
|
||||
error += QString(e.what()) + "1";
|
||||
return -1;
|
||||
}).fail([&](const std::exception& e) {
|
||||
error += QString(e.what()) + "2";
|
||||
return -1;
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(error, QString("foo1"));
|
||||
}
|
||||
|
||||
void tst_qpromise::failBaseClass()
|
||||
{
|
||||
// http://en.cppreference.com/w/cpp/error/exception
|
||||
auto p = QPromise<int>::reject(std::out_of_range("foo"));
|
||||
|
||||
QString error;
|
||||
p.fail([&](const std::runtime_error& e) {
|
||||
error += QString(e.what()) + "0";
|
||||
return -1;
|
||||
}).fail([&](const std::logic_error& e) {
|
||||
error += QString(e.what()) + "1";
|
||||
return -1;
|
||||
}).fail([&](const std::exception& e) {
|
||||
error += QString(e.what()) + "2";
|
||||
return -1;
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(error, QString("foo1"));
|
||||
}
|
||||
|
||||
void tst_qpromise::failCatchAll()
|
||||
{
|
||||
auto p = QPromise<int>::reject(std::out_of_range("foo"));
|
||||
|
||||
QString error;
|
||||
p.fail([&](const std::runtime_error& e) {
|
||||
error += QString(e.what()) + "0";
|
||||
return -1;
|
||||
}).fail([&]() {
|
||||
error += "bar";
|
||||
return -1;
|
||||
}).fail([&](const std::exception& e) {
|
||||
error += QString(e.what()) + "2";
|
||||
return -1;
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(error, QString("bar"));
|
||||
}
|
||||
|
||||
void tst_qpromise::finallyFulfilled()
|
||||
{
|
||||
int value = -1;
|
||||
auto p = QPromise<int>::resolve(42).finally([&]() {
|
||||
value = 8;
|
||||
return 16; // ignored!
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int> >::value));
|
||||
QCOMPARE(waitForValue(p, -1), 42);
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(value, 8);
|
||||
}
|
||||
|
||||
void tst_qpromise::finallyFulfilled_void()
|
||||
{
|
||||
int value = -1;
|
||||
auto p = QPromise<void>::resolve().finally([&]() {
|
||||
value = 8;
|
||||
return 16; // ignored!
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void> >::value));
|
||||
QCOMPARE(waitForValue(p, -1, 42), 42);
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(value, 8);
|
||||
}
|
||||
|
||||
void tst_qpromise::finallyRejected()
|
||||
{
|
||||
int value = -1;
|
||||
auto p = QPromise<int>::reject(QString("foo")).finally([&]() {
|
||||
value = 8;
|
||||
return 16; // ignored!
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int> >::value));
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(value, 8);
|
||||
}
|
||||
|
||||
void tst_qpromise::finallyRejected_void()
|
||||
{
|
||||
int value = -1;
|
||||
auto p = QPromise<void>::reject(QString("foo")).finally([&]() {
|
||||
value = 8;
|
||||
return 16; // ignored!
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void> >::value));
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(value, 8);
|
||||
}
|
||||
|
||||
void tst_qpromise::finallyThrows()
|
||||
{
|
||||
{ // fulfilled
|
||||
auto p = QPromise<int>::resolve(42).finally([&]() {
|
||||
throw QString("bar");
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int> >::value));
|
||||
QCOMPARE(waitForError(p, QString()), QString("bar"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
}
|
||||
{ // rejected
|
||||
auto p = QPromise<int>::reject(QString("foo")).finally([&]() {
|
||||
throw QString("bar");
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int> >::value));
|
||||
QCOMPARE(waitForError(p, QString()), QString("bar"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_qpromise::finallyThrows_void()
|
||||
{
|
||||
{ // fulfilled
|
||||
auto p = QPromise<void>::resolve().finally([&]() {
|
||||
throw QString("bar");
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void> >::value));
|
||||
QCOMPARE(waitForError(p, QString()), QString("bar"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
}
|
||||
{ // rejected
|
||||
auto p = QPromise<void>::reject(QString("foo")).finally([&]() {
|
||||
throw QString("bar");
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void> >::value));
|
||||
QCOMPARE(waitForError(p, QString()), QString("bar"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_qpromise::finallyDelayedResolved()
|
||||
{
|
||||
{ // fulfilled
|
||||
QVector<int> values;
|
||||
auto p = QPromise<int>::resolve(42).finally([&]() {
|
||||
QPromise<int> p([&](const QPromiseResolve<int>& resolve) {
|
||||
qtpromise_defer([=, &values]() {
|
||||
values << 64;
|
||||
resolve(16); // ignored!
|
||||
});
|
||||
});
|
||||
|
||||
values << 8;
|
||||
return p;
|
||||
});
|
||||
|
||||
QCOMPARE(waitForValue(p, -1), 42);
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(values, QVector<int>({8, 64}));
|
||||
}
|
||||
{ // rejected
|
||||
QVector<int> values;
|
||||
auto p = QPromise<int>::reject(QString("foo")).finally([&]() {
|
||||
QPromise<int> p([&](const QPromiseResolve<int>& resolve) {
|
||||
qtpromise_defer([=, &values]() {
|
||||
values << 64;
|
||||
resolve(16); // ignored!
|
||||
});
|
||||
});
|
||||
|
||||
values << 8;
|
||||
return p;
|
||||
});
|
||||
|
||||
p.then([&](int r) {
|
||||
values << r;
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(values, QVector<int>({8, 64}));
|
||||
}
|
||||
}
|
||||
|
||||
void tst_qpromise::finallyDelayedRejected()
|
||||
{
|
||||
{ // fulfilled
|
||||
auto p = QPromise<int>::resolve(42).finally([]() {
|
||||
return QPromise<int>([](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
|
||||
qtpromise_defer([=]() {
|
||||
reject(QString("bar"));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
QCOMPARE(waitForError(p, QString()), QString("bar"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
}
|
||||
{ // rejected
|
||||
auto p = QPromise<int>::reject(QString("foo")).finally([]() {
|
||||
return QPromise<int>([](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
|
||||
qtpromise_defer([=]() {
|
||||
reject(QString("bar"));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
QCOMPARE(waitForError(p, QString()), QString("bar"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_qpromise::tapFulfilled()
|
||||
{
|
||||
int value = -1;
|
||||
auto p = QPromise<int>::resolve(42).tap([&](int res) {
|
||||
value = res + 1;
|
||||
return 8;
|
||||
});
|
||||
|
||||
QCOMPARE(waitForValue(p, 42), 42);
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(value, 43);
|
||||
}
|
||||
|
||||
void tst_qpromise::tapFulfilled_void()
|
||||
{
|
||||
int value = -1;
|
||||
auto p = QPromise<void>::resolve().tap([&]() {
|
||||
value = 43;
|
||||
return 8;
|
||||
});
|
||||
|
||||
QCOMPARE(waitForValue(p, -1, 42), 42);
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(value, 43);
|
||||
}
|
||||
|
||||
void tst_qpromise::tapRejected()
|
||||
{
|
||||
int value = -1;
|
||||
auto p = QPromise<int>::reject(QString("foo")).tap([&](int res) {
|
||||
value = res + 1;
|
||||
});
|
||||
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(value, -1);
|
||||
}
|
||||
|
||||
void tst_qpromise::tapRejected_void()
|
||||
{
|
||||
int value = -1;
|
||||
auto p = QPromise<void>::reject(QString("foo")).tap([&]() {
|
||||
value = 43;
|
||||
});
|
||||
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(value, -1);
|
||||
}
|
||||
|
||||
void tst_qpromise::tapThrows()
|
||||
{
|
||||
auto p = QPromise<int>::resolve(42).tap([&](int) {
|
||||
throw QString("foo");
|
||||
});
|
||||
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
}
|
||||
|
||||
void tst_qpromise::tapThrows_void()
|
||||
{
|
||||
auto p = QPromise<void>::resolve().tap([&]() {
|
||||
throw QString("foo");
|
||||
});
|
||||
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
}
|
||||
|
||||
void tst_qpromise::tapDelayedResolved()
|
||||
{
|
||||
QVector<int> values;
|
||||
auto p = QPromise<int>::resolve(1).tap([&](int) {
|
||||
QPromise<int> p([&](const QPromiseResolve<int>& resolve) {
|
||||
qtpromise_defer([=, &values]() {
|
||||
values << 3;
|
||||
resolve(4); // ignored!
|
||||
});
|
||||
});
|
||||
|
||||
values << 2;
|
||||
return p;
|
||||
});
|
||||
|
||||
p.then([&](int r) {
|
||||
values << r;
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(values, QVector<int>({2, 3, 1}));
|
||||
}
|
||||
|
||||
void tst_qpromise::tapDelayedRejected()
|
||||
{
|
||||
QVector<int> values;
|
||||
auto p = QPromise<int>::resolve(1).tap([&](int) {
|
||||
QPromise<int> p([&](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
|
||||
qtpromise_defer([=, &values]() {
|
||||
values << 3;
|
||||
reject(QString("foo"));
|
||||
});
|
||||
});
|
||||
|
||||
values << 2;
|
||||
return p;
|
||||
});
|
||||
|
||||
p.then([&](int r) {
|
||||
values << r;
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(values, QVector<int>({2, 3}));
|
||||
}
|
||||
|
||||
void tst_qpromise::timeoutFulfilled()
|
||||
{
|
||||
QElapsedTimer timer;
|
||||
qint64 elapsed = -1;
|
||||
|
||||
timer.start();
|
||||
|
||||
auto p = QPromise<int>([](const QPromiseResolve<int>& resolve) {
|
||||
QTimer::singleShot(1000, [=]() {
|
||||
resolve(42);
|
||||
});
|
||||
}).timeout(2000).finally([&]() {
|
||||
elapsed = timer.elapsed();
|
||||
});
|
||||
|
||||
QCOMPARE(waitForValue(p, -1), 42);
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QVERIFY(elapsed < 2000);
|
||||
}
|
||||
|
||||
void tst_qpromise::timeoutRejected()
|
||||
{
|
||||
QElapsedTimer timer;
|
||||
qint64 elapsed = -1;
|
||||
|
||||
timer.start();
|
||||
|
||||
auto p = QPromise<int>([](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
|
||||
QTimer::singleShot(1000, [=]() {
|
||||
reject(QString("foo"));
|
||||
});
|
||||
}).timeout(2000).finally([&]() {
|
||||
elapsed = timer.elapsed();
|
||||
});
|
||||
|
||||
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QVERIFY(elapsed < 2000);
|
||||
}
|
||||
|
||||
void tst_qpromise::timeoutReject()
|
||||
{
|
||||
QElapsedTimer timer;
|
||||
qint64 elapsed = -1;
|
||||
bool failed = false;
|
||||
|
||||
timer.start();
|
||||
|
||||
auto p = QPromise<int>([](const QPromiseResolve<int>& resolve) {
|
||||
QTimer::singleShot(4000, [=]() {
|
||||
resolve(42);
|
||||
});
|
||||
}).timeout(2000).finally([&]() {
|
||||
elapsed = timer.elapsed();
|
||||
});
|
||||
|
||||
p.fail([&](const QPromiseTimeoutException&) {
|
||||
failed = true;
|
||||
return -1;
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(waitForValue(p, -1), -1);
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(failed, true);
|
||||
QVERIFY(elapsed >= 2000 * 0.95); // Qt::CoarseTimer (default) Coarse timers try to
|
||||
QVERIFY(elapsed <= 2000 * 1.05); // keep accuracy within 5% of the desired interval.
|
||||
}
|
||||
|
||||
void tst_qpromise::delayFulfilled()
|
||||
{
|
||||
QElapsedTimer timer;
|
||||
qint64 elapsed = -1;
|
||||
|
||||
timer.start();
|
||||
|
||||
auto p = QPromise<int>::resolve(42).delay(1000).finally([&]() {
|
||||
elapsed = timer.elapsed();
|
||||
});
|
||||
|
||||
QCOMPARE(waitForValue(p, -1), 42);
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QVERIFY(elapsed >= 1000 * 0.95); // Qt::CoarseTimer (default) Coarse timers try to
|
||||
QVERIFY(elapsed <= 1000 * 1.05); // keep accuracy within 5% of the desired interval.
|
||||
}
|
||||
|
||||
void tst_qpromise::delayRejected()
|
||||
{
|
||||
QElapsedTimer timer;
|
||||
qint64 elapsed = -1;
|
||||
|
||||
timer.start();
|
||||
|
||||
auto p = QPromise<int>::reject(QString("foo")).delay(1000).finally([&]() {
|
||||
elapsed = timer.elapsed();
|
||||
});
|
||||
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QVERIFY(elapsed < 5);
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
TARGET = tst_benchmark
|
||||
SOURCES += $$PWD/tst_benchmark.cpp
|
||||
|
||||
include(../tests.pri)
|
||||
include(../qtpromise.pri)
|
@ -4,6 +4,14 @@
|
||||
// Qt
|
||||
#include <QtTest>
|
||||
|
||||
#ifdef Q_CC_MSVC
|
||||
// MSVC calls the copy constructor on std::current_exception AND std::rethrow_exception
|
||||
// https://stackoverflow.com/a/31820854
|
||||
#define EXCEPT_CALL_COPY_CTOR 1
|
||||
#else
|
||||
#define EXCEPT_CALL_COPY_CTOR 0
|
||||
#endif
|
||||
|
||||
using namespace QtPromise;
|
||||
|
||||
class tst_benchmark : public QObject
|
||||
@ -12,6 +20,7 @@ class tst_benchmark : public QObject
|
||||
|
||||
private Q_SLOTS:
|
||||
void valueResolve();
|
||||
void valueResolveStatic();
|
||||
void valueReject();
|
||||
void valueThen();
|
||||
void valueFinally();
|
||||
@ -58,6 +67,35 @@ struct Data : public Logger
|
||||
Data(int v): Logger(), m_value(v) {}
|
||||
int value() const { return m_value; }
|
||||
|
||||
// MSVC 2013 doesn't support implicit generation of the move constructor and
|
||||
// operator, so we need to explicitly define these methods and thus the copy
|
||||
// constructor and operator also need to be explicitly defined (error C2280).
|
||||
// https://stackoverflow.com/a/26581337
|
||||
|
||||
Data(const Data& other)
|
||||
: Logger(other)
|
||||
, m_value(other.m_value)
|
||||
{ }
|
||||
|
||||
Data(Data&& other) : Logger(std::forward<Data>(other))
|
||||
{
|
||||
qSwap(m_value, other.m_value);
|
||||
}
|
||||
|
||||
Data& operator=(const Data& other)
|
||||
{
|
||||
Logger::operator=(other);
|
||||
m_value = other.m_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Data& operator=(Data&& other)
|
||||
{
|
||||
Logger::operator=(std::forward<Data>(other));
|
||||
qSwap(m_value, other.m_value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
int m_value;
|
||||
};
|
||||
@ -89,6 +127,31 @@ void tst_benchmark::valueResolve()
|
||||
}
|
||||
}
|
||||
|
||||
void tst_benchmark::valueResolveStatic()
|
||||
{
|
||||
{ // should move the value when resolved by rvalue
|
||||
Data::logs().reset();
|
||||
QPromise<Data>::resolve(Data(42)).wait();
|
||||
|
||||
QCOMPARE(Data::logs().ctor, 1);
|
||||
QCOMPARE(Data::logs().copy, 0);
|
||||
QCOMPARE(Data::logs().move, 1); // move value to the promise data
|
||||
QCOMPARE(Data::logs().refs, 0);
|
||||
}
|
||||
{ // should create one copy of the value when resolved by lvalue
|
||||
{
|
||||
Data::logs().reset();
|
||||
Data value(42);
|
||||
QPromise<Data>::resolve(value).wait();
|
||||
}
|
||||
|
||||
QCOMPARE(Data::logs().ctor, 1);
|
||||
QCOMPARE(Data::logs().copy, 1); // copy value to the promise data
|
||||
QCOMPARE(Data::logs().move, 0);
|
||||
QCOMPARE(Data::logs().refs, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_benchmark::valueReject()
|
||||
{
|
||||
{ // should not create any data if rejected
|
||||
@ -264,7 +327,7 @@ void tst_benchmark::errorReject()
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(Data::logs().ctor, 1);
|
||||
QCOMPARE(Data::logs().copy, 1); // copy value in std::exception_ptr
|
||||
QCOMPARE(Data::logs().copy, 1 + EXCEPT_CALL_COPY_CTOR); // copy value in std::exception_ptr
|
||||
QCOMPARE(Data::logs().move, 0);
|
||||
QCOMPARE(Data::logs().refs, 0);
|
||||
}
|
||||
@ -276,7 +339,7 @@ void tst_benchmark::errorReject()
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(Data::logs().ctor, 1);
|
||||
QCOMPARE(Data::logs().copy, 1); // copy value to the promise data
|
||||
QCOMPARE(Data::logs().copy, 1 + EXCEPT_CALL_COPY_CTOR); // copy value to the promise data
|
||||
QCOMPARE(Data::logs().move, 0);
|
||||
QCOMPARE(Data::logs().refs, 0);
|
||||
}
|
||||
@ -292,7 +355,7 @@ void tst_benchmark::errorThen()
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(Data::logs().ctor, 1);
|
||||
QCOMPARE(Data::logs().copy, 1); // (initial) copy value in std::exception_ptr
|
||||
QCOMPARE(Data::logs().copy, 1 + 2 * EXCEPT_CALL_COPY_CTOR); // (initial) copy value in std::exception_ptr
|
||||
QCOMPARE(Data::logs().move, 0);
|
||||
QCOMPARE(Data::logs().refs, 0);
|
||||
QCOMPARE(value, 42);
|
||||
@ -307,7 +370,7 @@ void tst_benchmark::errorThen()
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(Data::logs().ctor, 1);
|
||||
QCOMPARE(Data::logs().copy, 1); // (initial) copy value in std::exception_ptr
|
||||
QCOMPARE(Data::logs().copy, 1 + 4 * EXCEPT_CALL_COPY_CTOR); // (initial) copy value in std::exception_ptr
|
||||
QCOMPARE(Data::logs().move, 0);
|
||||
QCOMPARE(Data::logs().refs, 0);
|
||||
QCOMPARE(value, 42);
|
@ -2,4 +2,4 @@ QT += concurrent
|
||||
TARGET = tst_future
|
||||
SOURCES += $$PWD/tst_future.cpp
|
||||
|
||||
include(../tests.pri)
|
||||
include(../qtpromise.pri)
|
@ -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) {
|
||||
@ -261,9 +261,9 @@ void tst_future::fail()
|
||||
QString result;
|
||||
auto input = QPromise<QString>::reject(MyException("bar"));
|
||||
auto output = input.fail([](const MyException& e) {
|
||||
return QtConcurrent::run([=]() {
|
||||
return QString("foo") + e.error();
|
||||
});
|
||||
return QtConcurrent::run([](const QString& error) {
|
||||
return QString("foo%1").arg(error);
|
||||
}, e.error());
|
||||
});
|
||||
|
||||
QCOMPARE(input.isRejected(), true);
|
||||
@ -282,9 +282,9 @@ void tst_future::fail_void()
|
||||
QString result;
|
||||
auto input = QPromise<void>::reject(MyException("bar"));
|
||||
auto output = input.fail([&](const MyException& e) {
|
||||
return QtConcurrent::run([&]() {
|
||||
result = e.error();
|
||||
});
|
||||
return QtConcurrent::run([&](const QString& error) {
|
||||
result = error;
|
||||
}, e.error());
|
||||
});
|
||||
|
||||
QCOMPARE(input.isRejected(), true);
|
||||
@ -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);
|
4
tests/auto/qtpromise/helpers/all/all.pro
Normal file
4
tests/auto/qtpromise/helpers/all/all.pro
Normal file
@ -0,0 +1,4 @@
|
||||
TARGET = tst_helpers_all
|
||||
SOURCES += $$PWD/tst_all.cpp
|
||||
|
||||
include(../../qtpromise.pri)
|
221
tests/auto/qtpromise/helpers/all/tst_all.cpp
Normal file
221
tests/auto/qtpromise/helpers/all/tst_all.cpp
Normal file
@ -0,0 +1,221 @@
|
||||
// Tests
|
||||
#include "../../shared/utils.h"
|
||||
|
||||
// QtPromise
|
||||
#include <QtPromise>
|
||||
|
||||
// Qt
|
||||
#include <QtTest>
|
||||
|
||||
using namespace QtPromise;
|
||||
|
||||
class tst_helpers_all : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void emptySequence();
|
||||
void emptySequence_void();
|
||||
void allPromisesSucceed();
|
||||
void allPromisesSucceed_void();
|
||||
void atLeastOnePromiseReject();
|
||||
void atLeastOnePromiseReject_void();
|
||||
void preserveOrder();
|
||||
void sequenceTypes();
|
||||
void sequenceTypes_void();
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_helpers_all)
|
||||
#include "tst_all.moc"
|
||||
|
||||
namespace {
|
||||
|
||||
template <class Sequence>
|
||||
struct SequenceTester
|
||||
{
|
||||
Q_STATIC_ASSERT((std::is_same<typename Sequence::value_type, QPromise<int>>::value));
|
||||
|
||||
static void exec()
|
||||
{
|
||||
Sequence promises{
|
||||
QtPromise::qPromise(42),
|
||||
QtPromise::qPromise(43),
|
||||
QtPromise::qPromise(44)
|
||||
};
|
||||
|
||||
promises.push_back(QtPromise::qPromise(45));
|
||||
promises.insert(++promises.begin(), QtPromise::qPromise(46));
|
||||
promises.pop_back();
|
||||
|
||||
auto p = QtPromise::qPromiseAll(promises);
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(p.isPending(), true);
|
||||
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...>>
|
||||
{
|
||||
static void exec()
|
||||
{
|
||||
Sequence<QPromise<void>, Args...> promises{
|
||||
QtPromise::qPromise(),
|
||||
QtPromise::qPromise(),
|
||||
QtPromise::qPromise()
|
||||
};
|
||||
|
||||
promises.push_back(QtPromise::qPromise());
|
||||
promises.insert(++promises.begin(), QtPromise::qPromise());
|
||||
promises.pop_back();
|
||||
|
||||
auto p = QtPromise::qPromiseAll(promises);
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, -1, 42), 42);
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void tst_helpers_all::emptySequence()
|
||||
{
|
||||
auto p = QtPromise::qPromiseAll(QVector<QPromise<int>>());
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({}));
|
||||
}
|
||||
|
||||
void tst_helpers_all::emptySequence_void()
|
||||
{
|
||||
auto p = QtPromise::qPromiseAll(QVector<QPromise<void>>());
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(waitForValue(p, -1, 42), 42);
|
||||
}
|
||||
|
||||
void tst_helpers_all::allPromisesSucceed()
|
||||
{
|
||||
auto p0 = QtPromise::qPromise(42);
|
||||
auto p1 = QtPromise::qPromise(44);
|
||||
auto p2 = QPromise<int>([](const QPromiseResolve<int>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=](){
|
||||
resolve(43);
|
||||
});
|
||||
});
|
||||
|
||||
auto p = QtPromise::qPromiseAll(QVector<QPromise<int>>{p0, p2, p1});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(p0.isFulfilled(), true);
|
||||
QCOMPARE(p1.isFulfilled(), true);
|
||||
QCOMPARE(p2.isPending(), true);
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({42, 43, 44}));
|
||||
QCOMPARE(p2.isFulfilled(), true);
|
||||
}
|
||||
|
||||
void tst_helpers_all::allPromisesSucceed_void()
|
||||
{
|
||||
auto p0 = QtPromise::qPromise();
|
||||
auto p1 = QtPromise::qPromise();
|
||||
auto p2 = QPromise<void>([](const QPromiseResolve<void>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=](){
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
auto p = QtPromise::qPromiseAll(QVector<QPromise<void>>{p0, p2, p1});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
|
||||
QCOMPARE(p0.isFulfilled(), true);
|
||||
QCOMPARE(p1.isFulfilled(), true);
|
||||
QCOMPARE(p2.isPending(), true);
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, -1, 42), 42);
|
||||
QCOMPARE(p2.isFulfilled(), true);
|
||||
}
|
||||
|
||||
void tst_helpers_all::atLeastOnePromiseReject()
|
||||
{
|
||||
auto p0 = QtPromise::qPromise(42);
|
||||
auto p1 = QtPromise::qPromise(44);
|
||||
auto p2 = QPromise<int>([](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
|
||||
QtPromisePrivate::qtpromise_defer([=](){
|
||||
reject(QString("foo"));
|
||||
});
|
||||
});
|
||||
|
||||
auto p = QtPromise::qPromiseAll(QVector<QPromise<int>>{p0, p2, p1});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(p0.isFulfilled(), true);
|
||||
QCOMPARE(p1.isFulfilled(), true);
|
||||
QCOMPARE(p2.isPending(), true);
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
QCOMPARE(p2.isRejected(), true);
|
||||
}
|
||||
|
||||
void tst_helpers_all::atLeastOnePromiseReject_void()
|
||||
{
|
||||
auto p0 = QtPromise::qPromise();
|
||||
auto p1 = QtPromise::qPromise();
|
||||
auto p2 = QPromise<void>([](const QPromiseResolve<void>&, const QPromiseReject<void>& reject) {
|
||||
QtPromisePrivate::qtpromise_defer([=](){
|
||||
reject(QString("foo"));
|
||||
});
|
||||
});
|
||||
|
||||
auto p = QtPromise::qPromiseAll(QVector<QPromise<void>>{p0, p2, p1});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
|
||||
QCOMPARE(p0.isFulfilled(), true);
|
||||
QCOMPARE(p1.isFulfilled(), true);
|
||||
QCOMPARE(p2.isPending(), true);
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
QCOMPARE(p2.isRejected(), true);
|
||||
}
|
||||
|
||||
void tst_helpers_all::preserveOrder()
|
||||
{
|
||||
auto p0 = QtPromise::qPromise(42).delay(500);
|
||||
auto p1 = QtPromise::qPromise(43).delay(100);
|
||||
auto p2 = QtPromise::qPromise(44).delay(250);
|
||||
|
||||
auto p = QtPromise::qPromiseAll(QVector<QPromise<int>>{p0, p1, p2});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(p0.isPending(), true);
|
||||
QCOMPARE(p1.isPending(), true);
|
||||
QCOMPARE(p2.isPending(), true);
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({42, 43, 44}));
|
||||
QCOMPARE(p0.isFulfilled(), true);
|
||||
QCOMPARE(p1.isFulfilled(), true);
|
||||
QCOMPARE(p2.isFulfilled(), true);
|
||||
}
|
||||
|
||||
// QVector::push_back/append isn't supported since it requires a default
|
||||
// constructor (see https://github.com/simonbrunel/qtpromise/issues/3)
|
||||
|
||||
void tst_helpers_all::sequenceTypes()
|
||||
{
|
||||
SequenceTester<QList<QPromise<int>>>::exec();
|
||||
//SequenceTester<QVector<QPromise<int>>>::exec();
|
||||
SequenceTester<std::list<QPromise<int>>>::exec();
|
||||
SequenceTester<std::vector<QPromise<int>>>::exec();
|
||||
}
|
||||
|
||||
void tst_helpers_all::sequenceTypes_void()
|
||||
{
|
||||
SequenceTester<QList<QPromise<void>>>::exec();
|
||||
//SequenceTester<QVector<QPromise<void>>>::exec();
|
||||
SequenceTester<std::list<QPromise<void>>>::exec();
|
||||
SequenceTester<std::vector<QPromise<void>>>::exec();
|
||||
}
|
5
tests/auto/qtpromise/helpers/attempt/attempt.pro
Normal file
5
tests/auto/qtpromise/helpers/attempt/attempt.pro
Normal file
@ -0,0 +1,5 @@
|
||||
QT += concurrent
|
||||
TARGET = tst_helpers_attempt
|
||||
SOURCES += $$PWD/tst_attempt.cpp
|
||||
|
||||
include(../../qtpromise.pri)
|
99
tests/auto/qtpromise/helpers/attempt/tst_attempt.cpp
Normal file
99
tests/auto/qtpromise/helpers/attempt/tst_attempt.cpp
Normal file
@ -0,0 +1,99 @@
|
||||
// Tests
|
||||
#include "../../shared/utils.h"
|
||||
|
||||
// QtPromise
|
||||
#include <QtPromise>
|
||||
|
||||
// Qt
|
||||
#include <QtConcurrent>
|
||||
#include <QtTest>
|
||||
|
||||
// STL
|
||||
#include <memory>
|
||||
|
||||
using namespace QtPromise;
|
||||
|
||||
class tst_helpers_attempt : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void voidResult();
|
||||
void typedResult();
|
||||
void futureResult();
|
||||
void promiseResult();
|
||||
void functorThrows();
|
||||
void callWithParams();
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_helpers_attempt)
|
||||
#include "tst_attempt.moc"
|
||||
|
||||
void tst_helpers_attempt::voidResult()
|
||||
{
|
||||
auto p = QtPromise::attempt([]() {});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(waitForValue(p, -1, 42), 42);
|
||||
}
|
||||
|
||||
void tst_helpers_attempt::typedResult()
|
||||
{
|
||||
auto p = QtPromise::attempt([]() {
|
||||
return QString("foo");
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QString>>::value));
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(waitForValue(p, QString()), QString("foo"));
|
||||
}
|
||||
|
||||
void tst_helpers_attempt::futureResult()
|
||||
{
|
||||
auto p = QtPromise::attempt([]() {
|
||||
return QtConcurrent::run([]() {
|
||||
return QString("foo");
|
||||
});
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QString>>::value));
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, QString()), QString("foo"));
|
||||
}
|
||||
|
||||
void tst_helpers_attempt::promiseResult()
|
||||
{
|
||||
auto p = QtPromise::attempt([]() {
|
||||
return QtPromise::qPromise(42).delay(200);
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int>>::value));
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, -1), 42);
|
||||
}
|
||||
|
||||
void tst_helpers_attempt::functorThrows()
|
||||
{
|
||||
auto p = QtPromise::attempt([]() {
|
||||
if (true) {
|
||||
throw QString("bar");
|
||||
}
|
||||
return 42;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int>>::value));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(waitForError(p, QString()), QString("bar"));
|
||||
}
|
||||
|
||||
void tst_helpers_attempt::callWithParams()
|
||||
{
|
||||
auto p = QtPromise::attempt([&](int i, const QString& s) {
|
||||
return QString("%1:%2").arg(i).arg(s);
|
||||
}, 42, "foo");
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QString>>::value));
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(waitForValue(p, QString()), QString("42:foo"));
|
||||
}
|
4
tests/auto/qtpromise/helpers/each/each.pro
Normal file
4
tests/auto/qtpromise/helpers/each/each.pro
Normal file
@ -0,0 +1,4 @@
|
||||
TARGET = tst_qpromise_each
|
||||
SOURCES += $$PWD/tst_each.cpp
|
||||
|
||||
include(../../qtpromise.pri)
|
154
tests/auto/qtpromise/helpers/each/tst_each.cpp
Normal file
154
tests/auto/qtpromise/helpers/each/tst_each.cpp
Normal file
@ -0,0 +1,154 @@
|
||||
// Tests
|
||||
#include "../../shared/utils.h"
|
||||
|
||||
// QtPromise
|
||||
#include <QtPromise>
|
||||
|
||||
// Qt
|
||||
#include <QtTest>
|
||||
|
||||
using namespace QtPromise;
|
||||
|
||||
class tst_helpers_each : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void emptySequence();
|
||||
void preserveValues();
|
||||
void ignoreResult();
|
||||
void delayedFulfilled();
|
||||
void delayedRejected();
|
||||
void functorThrows();
|
||||
void functorArguments();
|
||||
void sequenceTypes();
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_helpers_each)
|
||||
#include "tst_each.moc"
|
||||
|
||||
namespace {
|
||||
|
||||
template <class Sequence>
|
||||
struct SequenceTester
|
||||
{
|
||||
static void exec()
|
||||
{
|
||||
QVector<int> values;
|
||||
auto p = QtPromise::each(Sequence{42, 43, 44}, [&](int v, int i) {
|
||||
values << i << v;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<Sequence>>::value));
|
||||
QCOMPARE(waitForValue(p, Sequence()), Sequence({42, 43, 44}));
|
||||
QCOMPARE(values, QVector<int>({0, 42, 1, 43, 2, 44}));
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void tst_helpers_each::emptySequence()
|
||||
{
|
||||
QVector<int> values;
|
||||
auto p = QtPromise::each(QVector<int>{}, [&](int v, ...) {
|
||||
values << v;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>());
|
||||
QCOMPARE(values, QVector<int>({}));
|
||||
}
|
||||
|
||||
void tst_helpers_each::preserveValues()
|
||||
{
|
||||
QVector<int> values;
|
||||
auto p = QtPromise::each(QVector<int>{42, 43, 44}, [&](int v, ...) {
|
||||
values << v + 1;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({42, 43, 44}));
|
||||
QCOMPARE(values, QVector<int>({43, 44, 45}));
|
||||
}
|
||||
|
||||
void tst_helpers_each::ignoreResult()
|
||||
{
|
||||
QVector<int> values;
|
||||
auto p = QtPromise::each(QVector<int>{42, 43, 44}, [&](int v, ...) {
|
||||
values << v + 1;
|
||||
return "Foo";
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({42, 43, 44}));
|
||||
QCOMPARE(values, QVector<int>({43, 44, 45}));
|
||||
}
|
||||
|
||||
void tst_helpers_each::delayedFulfilled()
|
||||
{
|
||||
QMap<int, int> values;
|
||||
auto p = QtPromise::each(QVector<int>{42, 43, 44}, [&](int v, int index) {
|
||||
return QPromise<int>([&](const QPromiseResolve<int>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=, &values]() {
|
||||
values[v] = index;
|
||||
resolve(42);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({42, 43, 44}));
|
||||
QMap<int, int> expected{{42, 0}, {43, 1}, {44, 2}};
|
||||
QCOMPARE(values, expected);
|
||||
}
|
||||
|
||||
void tst_helpers_each::delayedRejected()
|
||||
{
|
||||
auto p = QtPromise::each(QVector<int>{42, 43, 44}, [](int v, ...) {
|
||||
return QPromise<int>([&](
|
||||
const QPromiseResolve<int>& resolve,
|
||||
const QPromiseReject<int>& reject) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
if (v == 43) {
|
||||
reject(QString("foo"));
|
||||
}
|
||||
resolve(v);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
}
|
||||
|
||||
void tst_helpers_each::functorThrows()
|
||||
{
|
||||
auto p = QtPromise::each(QVector<int>{42, 43, 44}, [](int v, ...) {
|
||||
if (v == 44) {
|
||||
throw QString("foo");
|
||||
}
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
}
|
||||
|
||||
void tst_helpers_each::functorArguments()
|
||||
{
|
||||
QVector<int> values;
|
||||
auto p = QtPromise::each(QVector<int>{42, 43, 44}, [&](int v, int i) {
|
||||
values << i << v;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({42, 43, 44}));
|
||||
QCOMPARE(values, QVector<int>({0, 42, 1, 43, 2, 44}));
|
||||
}
|
||||
|
||||
void tst_helpers_each::sequenceTypes()
|
||||
{
|
||||
SequenceTester<QList<int>>::exec();
|
||||
SequenceTester<QVector<int>>::exec();
|
||||
SequenceTester<std::list<int>>::exec();
|
||||
SequenceTester<std::vector<int>>::exec();
|
||||
}
|
4
tests/auto/qtpromise/helpers/filter/filter.pro
Normal file
4
tests/auto/qtpromise/helpers/filter/filter.pro
Normal file
@ -0,0 +1,4 @@
|
||||
TARGET = tst_helpers_filter
|
||||
SOURCES += $$PWD/tst_filter.cpp
|
||||
|
||||
include(../../qtpromise.pri)
|
147
tests/auto/qtpromise/helpers/filter/tst_filter.cpp
Normal file
147
tests/auto/qtpromise/helpers/filter/tst_filter.cpp
Normal file
@ -0,0 +1,147 @@
|
||||
// Tests
|
||||
#include "../../shared/utils.h"
|
||||
|
||||
// QtPromise
|
||||
#include <QtPromise>
|
||||
|
||||
// Qt
|
||||
#include <QtTest>
|
||||
|
||||
using namespace QtPromise;
|
||||
|
||||
class tst_helpers_filter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void emptySequence();
|
||||
void filterValues();
|
||||
void delayedFulfilled();
|
||||
void delayedRejected();
|
||||
void functorThrows();
|
||||
void functorArguments();
|
||||
void preserveOrder();
|
||||
void sequenceTypes();
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_helpers_filter)
|
||||
#include "tst_filter.moc"
|
||||
|
||||
namespace {
|
||||
|
||||
template <class Sequence>
|
||||
struct SequenceTester
|
||||
{
|
||||
static void exec()
|
||||
{
|
||||
auto inputs = Sequence{42, 43, 44, 45, 46, 47, 48, 49, 50, 51};
|
||||
auto p = QtPromise::filter(inputs, [](int v, ...) {
|
||||
return v % 3 == 0;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<Sequence>>::value));
|
||||
QCOMPARE(waitForValue(p, Sequence()), Sequence({42, 45, 48, 51}));
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
|
||||
void tst_helpers_filter::emptySequence()
|
||||
{
|
||||
auto p = QtPromise::filter(QVector<int>{}, [](int v, ...) {
|
||||
return v % 2 == 0;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>{});
|
||||
}
|
||||
|
||||
void tst_helpers_filter::filterValues()
|
||||
{
|
||||
auto p = QtPromise::filter(QVector<int>{42, 43, 44}, [](int v, ...) {
|
||||
return v % 2 == 0;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({42, 44}));
|
||||
}
|
||||
|
||||
void tst_helpers_filter::delayedFulfilled()
|
||||
{
|
||||
auto p = QtPromise::filter(QVector<int>{42, 43, 44}, [](int v, ...) {
|
||||
return QPromise<bool>([&](const QPromiseResolve<bool>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
resolve(v % 2 == 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({42, 44}));
|
||||
}
|
||||
|
||||
void tst_helpers_filter::delayedRejected()
|
||||
{
|
||||
auto p = QtPromise::filter(QVector<int>{42, 43, 44}, [](int v, ...) {
|
||||
return QPromise<bool>([&](
|
||||
const QPromiseResolve<bool>& resolve,
|
||||
const QPromiseReject<bool>& reject) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
if (v == 44) {
|
||||
reject(QString("foo"));
|
||||
}
|
||||
resolve(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
}
|
||||
|
||||
void tst_helpers_filter::functorThrows()
|
||||
{
|
||||
auto p = QtPromise::filter(QVector<int>{42, 43, 44}, [](int v, ...) {
|
||||
if (v == 44) {
|
||||
throw QString("foo");
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
}
|
||||
|
||||
void tst_helpers_filter::functorArguments()
|
||||
{
|
||||
QMap<int, int> args;
|
||||
auto p = QtPromise::filter(QVector<int>{42, 43, 44}, [&](int v, int i) {
|
||||
args[v] = i;
|
||||
return i % 2 == 0;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({42, 44}));
|
||||
QMap<int, int> expected{{42, 0}, {43, 1}, {44, 2}};
|
||||
QCOMPARE(args, expected);
|
||||
}
|
||||
|
||||
void tst_helpers_filter::preserveOrder()
|
||||
{
|
||||
auto p = QtPromise::filter(QVector<int>{500, 100, 300, 250, 400}, [](int v, ...) {
|
||||
return QPromise<bool>::resolve(v > 200).delay(v);
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({500, 300, 250, 400}));
|
||||
}
|
||||
|
||||
void tst_helpers_filter::sequenceTypes()
|
||||
{
|
||||
SequenceTester<QList<int>>::exec();
|
||||
SequenceTester<QVector<int>>::exec();
|
||||
SequenceTester<std::list<int>>::exec();
|
||||
SequenceTester<std::vector<int>>::exec();
|
||||
}
|
9
tests/auto/qtpromise/helpers/helpers.pro
Normal file
9
tests/auto/qtpromise/helpers/helpers.pro
Normal file
@ -0,0 +1,9 @@
|
||||
TEMPLATE = subdirs
|
||||
SUBDIRS += \
|
||||
all \
|
||||
attempt \
|
||||
each \
|
||||
filter \
|
||||
map \
|
||||
reject \
|
||||
resolve
|
4
tests/auto/qtpromise/helpers/map/map.pro
Normal file
4
tests/auto/qtpromise/helpers/map/map.pro
Normal file
@ -0,0 +1,4 @@
|
||||
TARGET = tst_helpers_map
|
||||
SOURCES += $$PWD/tst_map.cpp
|
||||
|
||||
include(../../qtpromise.pri)
|
151
tests/auto/qtpromise/helpers/map/tst_map.cpp
Normal file
151
tests/auto/qtpromise/helpers/map/tst_map.cpp
Normal file
@ -0,0 +1,151 @@
|
||||
// Tests
|
||||
#include "../../shared/utils.h"
|
||||
|
||||
// QtPromise
|
||||
#include <QtPromise>
|
||||
|
||||
// Qt
|
||||
#include <QtTest>
|
||||
|
||||
using namespace QtPromise;
|
||||
|
||||
class tst_helpers_map : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void emptySequence();
|
||||
void modifyValues();
|
||||
void convertValues();
|
||||
void delayedFulfilled();
|
||||
void delayedRejected();
|
||||
void functorThrows();
|
||||
void functorArguments();
|
||||
void preserveOrder();
|
||||
void sequenceTypes();
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_helpers_map)
|
||||
#include "tst_map.moc"
|
||||
|
||||
namespace {
|
||||
|
||||
template <class Sequence>
|
||||
struct SequenceTester
|
||||
{
|
||||
static void exec()
|
||||
{
|
||||
auto p = QtPromise::map(Sequence{42, 43, 44}, [](int v, ...) {
|
||||
return QString::number(v + 1);
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<QString>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<QString>()), QVector<QString>({"43", "44", "45"}));
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void tst_helpers_map::emptySequence()
|
||||
{
|
||||
auto p = QtPromise::map(QVector<int>{}, [](int v, ...) {
|
||||
return v + 1;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({}));
|
||||
}
|
||||
|
||||
void tst_helpers_map::modifyValues()
|
||||
{
|
||||
auto p = QtPromise::map(QVector<int>{42, 43, 44}, [](int v, ...) {
|
||||
return v + 1;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({43, 44, 45}));
|
||||
}
|
||||
|
||||
void tst_helpers_map::convertValues()
|
||||
{
|
||||
auto p = QtPromise::map(QVector<int>{42, 43, 44}, [](int v, ...) {
|
||||
return QString::number(v + 1);
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<QString>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<QString>()), QVector<QString>({"43", "44", "45"}));
|
||||
}
|
||||
|
||||
void tst_helpers_map::delayedFulfilled()
|
||||
{
|
||||
auto p = QtPromise::map(QVector<int>{42, 43, 44}, [](int v, ...) {
|
||||
return QPromise<int>([&](const QPromiseResolve<int>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
resolve(v + 1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({43, 44, 45}));
|
||||
}
|
||||
|
||||
void tst_helpers_map::delayedRejected()
|
||||
{
|
||||
auto p = QtPromise::map(QVector<int>{42, 43, 44}, [](int v, ...) {
|
||||
return QPromise<int>([&](
|
||||
const QPromiseResolve<int>& resolve,
|
||||
const QPromiseReject<int>& reject) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
if (v == 43) {
|
||||
reject(QString("foo"));
|
||||
}
|
||||
resolve(v);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
}
|
||||
|
||||
void tst_helpers_map::functorThrows()
|
||||
{
|
||||
auto p = QtPromise::map(QVector<int>{42, 43, 44}, [](int v, ...) {
|
||||
if (v == 43) {
|
||||
throw QString("foo");
|
||||
}
|
||||
return v + 1;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
}
|
||||
|
||||
void tst_helpers_map::functorArguments()
|
||||
{
|
||||
auto p = QtPromise::map(QVector<int>{42, 42, 42}, [](int v, int i) {
|
||||
return v * i;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({0, 42, 84}));
|
||||
}
|
||||
|
||||
void tst_helpers_map::preserveOrder()
|
||||
{
|
||||
auto p = QtPromise::map(QVector<int>{500, 100, 250}, [](int v, ...) {
|
||||
return QPromise<int>::resolve(v + 1).delay(v);
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({501, 101, 251}));
|
||||
}
|
||||
|
||||
void tst_helpers_map::sequenceTypes()
|
||||
{
|
||||
SequenceTester<QList<int>>::exec();
|
||||
SequenceTester<QVector<int>>::exec();
|
||||
SequenceTester<std::list<int>>::exec();
|
||||
SequenceTester<std::vector<int>>::exec();
|
||||
}
|
4
tests/auto/qtpromise/helpers/reject/reject.pro
Normal file
4
tests/auto/qtpromise/helpers/reject/reject.pro
Normal file
@ -0,0 +1,4 @@
|
||||
TARGET = tst_helpers_reject
|
||||
SOURCES += $$PWD/tst_reject.cpp
|
||||
|
||||
include(../../qtpromise.pri)
|
74
tests/auto/qtpromise/helpers/reject/tst_reject.cpp
Normal file
74
tests/auto/qtpromise/helpers/reject/tst_reject.cpp
Normal file
@ -0,0 +1,74 @@
|
||||
// Tests
|
||||
#include "../../shared/utils.h"
|
||||
|
||||
// QtPromise
|
||||
#include <QtPromise>
|
||||
|
||||
// Qt
|
||||
#include <QtTest>
|
||||
|
||||
// STL
|
||||
#include <memory>
|
||||
|
||||
using namespace QtPromise;
|
||||
|
||||
class tst_helpers_reject : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void rejectWithValue();
|
||||
void rejectWithQSharedPtr();
|
||||
void rejectWithStdSharedPtr();
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_helpers_reject)
|
||||
#include "tst_reject.moc"
|
||||
|
||||
void tst_helpers_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_helpers_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_helpers_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);
|
||||
}
|
4
tests/auto/qtpromise/helpers/resolve/resolve.pro
Normal file
4
tests/auto/qtpromise/helpers/resolve/resolve.pro
Normal file
@ -0,0 +1,4 @@
|
||||
TARGET = tst_helpers_resolve
|
||||
SOURCES += $$PWD/tst_resolve.cpp
|
||||
|
||||
include(../../qtpromise.pri)
|
125
tests/auto/qtpromise/helpers/resolve/tst_resolve.cpp
Normal file
125
tests/auto/qtpromise/helpers/resolve/tst_resolve.cpp
Normal file
@ -0,0 +1,125 @@
|
||||
// Tests
|
||||
#include "../../shared/utils.h"
|
||||
|
||||
// QtPromise
|
||||
#include <QtPromise>
|
||||
|
||||
// Qt
|
||||
#include <QtTest>
|
||||
|
||||
// STL
|
||||
#include <memory>
|
||||
|
||||
using namespace QtPromise;
|
||||
|
||||
class tst_helpers_resolve : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void resolveWithValue();
|
||||
void resolveWithNoValue();
|
||||
void resolveWithTypedPromise();
|
||||
void resolveWithVoidPromise();
|
||||
void resolveWithQSharedPtr();
|
||||
void resolveWithStdSharedPtr();
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_helpers_resolve)
|
||||
#include "tst_resolve.moc"
|
||||
|
||||
void tst_helpers_resolve::resolveWithValue()
|
||||
{
|
||||
const int value = 42;
|
||||
auto p0 = QPromise<int>::resolve(value);
|
||||
auto p1 = QPromise<int>::resolve(43);
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p0), QPromise<int>>::value));
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p1), QPromise<int>>::value));
|
||||
QCOMPARE(p0.isFulfilled(), true);
|
||||
QCOMPARE(p1.isFulfilled(), true);
|
||||
QCOMPARE(waitForValue(p0, -1), 42);
|
||||
QCOMPARE(waitForValue(p1, -1), 43);
|
||||
}
|
||||
|
||||
void tst_helpers_resolve::resolveWithNoValue()
|
||||
{
|
||||
auto p = QPromise<void>::resolve();
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(waitForValue(p, -1, 42), 42);
|
||||
}
|
||||
|
||||
void tst_helpers_resolve::resolveWithTypedPromise()
|
||||
{
|
||||
auto p = QtPromise::qPromise(
|
||||
QPromise<QString>([](const QPromiseResolve<QString>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=](){
|
||||
resolve("foo");
|
||||
});
|
||||
}));
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QString>>::value));
|
||||
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, QString()), QString("foo"));
|
||||
}
|
||||
|
||||
void tst_helpers_resolve::resolveWithVoidPromise()
|
||||
{
|
||||
int check;
|
||||
auto p = QtPromise::qPromise(
|
||||
QPromise<void>([&](const QPromiseResolve<void>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=, &check](){
|
||||
check = 8;
|
||||
resolve();
|
||||
});
|
||||
}));
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
|
||||
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, -1, 42), 42);
|
||||
QCOMPARE(check, 8);
|
||||
}
|
||||
|
||||
// https://github.com/simonbrunel/qtpromise/issues/6
|
||||
void tst_helpers_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_helpers_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);
|
||||
}
|
4
tests/auto/qtpromise/qpromise/construct/construct.pro
Normal file
4
tests/auto/qtpromise/qpromise/construct/construct.pro
Normal file
@ -0,0 +1,4 @@
|
||||
TARGET = tst_qpromise_construct
|
||||
SOURCES += $$PWD/tst_construct.cpp
|
||||
|
||||
include(../../qtpromise.pri)
|
309
tests/auto/qtpromise/qpromise/construct/tst_construct.cpp
Normal file
309
tests/auto/qtpromise/qpromise/construct/tst_construct.cpp
Normal file
@ -0,0 +1,309 @@
|
||||
// Tests
|
||||
#include "../../shared/utils.h"
|
||||
|
||||
// QtPromise
|
||||
#include <QtPromise>
|
||||
|
||||
// Qt
|
||||
#include <QtTest>
|
||||
|
||||
// STL
|
||||
#include <memory>
|
||||
|
||||
using namespace QtPromise;
|
||||
|
||||
class tst_qpromise_construct : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void resolveSyncOneArg();
|
||||
void resolveSyncOneArg_void();
|
||||
void resolveSyncTwoArgs();
|
||||
void resolveSyncTwoArgs_void();
|
||||
void resolveAsyncOneArg();
|
||||
void resolveAsyncOneArg_void();
|
||||
void resolveAsyncTwoArgs();
|
||||
void resolveAsyncTwoArgs_void();
|
||||
void rejectThrowOneArg();
|
||||
void rejectThrowOneArg_void();
|
||||
void rejectThrowTwoArgs();
|
||||
void rejectThrowTwoArgs_void();
|
||||
void rejectSync();
|
||||
void rejectSync_void();
|
||||
void rejectAsync();
|
||||
void rejectAsync_void();
|
||||
void connectAndResolve();
|
||||
void connectAndReject();
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_qpromise_construct)
|
||||
#include "tst_construct.moc"
|
||||
|
||||
void tst_qpromise_construct::resolveSyncOneArg()
|
||||
{
|
||||
QPromise<int> p([](const QPromiseResolve<int>& resolve) {
|
||||
resolve(42);
|
||||
});
|
||||
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(waitForError(p, QString()), QString());
|
||||
QCOMPARE(waitForValue(p, -1), 42);
|
||||
}
|
||||
|
||||
void tst_qpromise_construct::resolveSyncOneArg_void()
|
||||
{
|
||||
QPromise<void> p([](const QPromiseResolve<void>& resolve) {
|
||||
resolve();
|
||||
});
|
||||
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(waitForError(p, QString()), QString());
|
||||
QCOMPARE(waitForValue(p, -1, 42), 42);
|
||||
}
|
||||
|
||||
void tst_qpromise_construct::resolveSyncTwoArgs()
|
||||
{
|
||||
QPromise<int> p([](const QPromiseResolve<int>& resolve, const QPromiseReject<int>&) {
|
||||
resolve(42);
|
||||
});
|
||||
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(waitForError(p, QString()), QString());
|
||||
QCOMPARE(waitForValue(p, -1), 42);
|
||||
}
|
||||
|
||||
void tst_qpromise_construct::resolveSyncTwoArgs_void()
|
||||
{
|
||||
QPromise<void> p([](const QPromiseResolve<void>& resolve, const QPromiseReject<void>&) {
|
||||
resolve();
|
||||
});
|
||||
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(waitForError(p, QString()), QString());
|
||||
QCOMPARE(waitForValue(p, -1, 42), 42);
|
||||
}
|
||||
|
||||
void tst_qpromise_construct::resolveAsyncOneArg()
|
||||
{
|
||||
QPromise<int> p([](const QPromiseResolve<int>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
resolve(42);
|
||||
});
|
||||
});
|
||||
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForError(p, QString()), QString());
|
||||
QCOMPARE(waitForValue(p, -1), 42);
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
}
|
||||
|
||||
void tst_qpromise_construct::resolveAsyncOneArg_void()
|
||||
{
|
||||
QPromise<void> p([](const QPromiseResolve<void>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForError(p, QString()), QString());
|
||||
QCOMPARE(waitForValue(p, -1, 42), 42);
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
}
|
||||
|
||||
void tst_qpromise_construct::resolveAsyncTwoArgs()
|
||||
{
|
||||
QPromise<int> p([](const QPromiseResolve<int>& resolve, const QPromiseReject<int>&) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
resolve(42);
|
||||
});
|
||||
});
|
||||
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForError(p, QString()), QString());
|
||||
QCOMPARE(waitForValue(p, -1), 42);
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
}
|
||||
|
||||
void tst_qpromise_construct::resolveAsyncTwoArgs_void()
|
||||
{
|
||||
QPromise<void> p([](const QPromiseResolve<void>& resolve, const QPromiseReject<void>&) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForError(p, QString()), QString());
|
||||
QCOMPARE(waitForValue(p, -1, 42), 42);
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
}
|
||||
|
||||
void tst_qpromise_construct::rejectSync()
|
||||
{
|
||||
QPromise<int> p([](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
|
||||
reject(QString("foo"));
|
||||
});
|
||||
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(waitForValue(p, -1), -1);
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
}
|
||||
|
||||
void tst_qpromise_construct::rejectSync_void()
|
||||
{
|
||||
QPromise<void> p([](const QPromiseResolve<void>&, const QPromiseReject<void>& reject) {
|
||||
reject(QString("foo"));
|
||||
});
|
||||
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(waitForValue(p, -1, 42), -1);
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
}
|
||||
|
||||
void tst_qpromise_construct::rejectAsync()
|
||||
{
|
||||
QPromise<int> p([](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
reject(QString("foo"));
|
||||
});
|
||||
});
|
||||
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, -1), -1);
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
}
|
||||
|
||||
void tst_qpromise_construct::rejectAsync_void()
|
||||
{
|
||||
QPromise<void> p([](const QPromiseResolve<void>&, const QPromiseReject<void>& reject) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
reject(QString("foo"));
|
||||
});
|
||||
});
|
||||
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, -1, 42), -1);
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
}
|
||||
|
||||
void tst_qpromise_construct::rejectThrowOneArg()
|
||||
{
|
||||
QPromise<int> p([](const QPromiseResolve<int>&) {
|
||||
throw QString("foo");
|
||||
});
|
||||
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(waitForValue(p, -1), -1);
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
}
|
||||
|
||||
void tst_qpromise_construct::rejectThrowOneArg_void()
|
||||
{
|
||||
QPromise<void> p([](const QPromiseResolve<void>&) {
|
||||
throw QString("foo");
|
||||
});
|
||||
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(waitForValue(p, -1, 42), -1);
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
}
|
||||
|
||||
void tst_qpromise_construct::rejectThrowTwoArgs()
|
||||
{
|
||||
QPromise<int> p([](const QPromiseResolve<int>&, const QPromiseReject<int>&) {
|
||||
throw QString("foo");
|
||||
});
|
||||
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(waitForValue(p, -1), -1);
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
}
|
||||
|
||||
void tst_qpromise_construct::rejectThrowTwoArgs_void()
|
||||
{
|
||||
QPromise<void> p([](const QPromiseResolve<void>&, const QPromiseReject<void>&) {
|
||||
throw QString("foo");
|
||||
});
|
||||
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
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);
|
||||
}
|
4
tests/auto/qtpromise/qpromise/delay/delay.pro
Normal file
4
tests/auto/qtpromise/qpromise/delay/delay.pro
Normal file
@ -0,0 +1,4 @@
|
||||
TARGET = tst_qpromise_delay
|
||||
SOURCES += $$PWD/tst_delay.cpp
|
||||
|
||||
include(../../qtpromise.pri)
|
59
tests/auto/qtpromise/qpromise/delay/tst_delay.cpp
Normal file
59
tests/auto/qtpromise/qpromise/delay/tst_delay.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
// Tests
|
||||
#include "../../shared/utils.h"
|
||||
|
||||
// QtPromise
|
||||
#include <QtPromise>
|
||||
|
||||
// Qt
|
||||
#include <QtTest>
|
||||
|
||||
using namespace QtPromise;
|
||||
|
||||
class tst_qpromise_delay : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void fulfilled();
|
||||
void rejected();
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_qpromise_delay)
|
||||
#include "tst_delay.moc"
|
||||
|
||||
void tst_qpromise_delay::fulfilled()
|
||||
{
|
||||
QElapsedTimer timer;
|
||||
qint64 elapsed = -1;
|
||||
|
||||
timer.start();
|
||||
|
||||
auto p = QPromise<int>::resolve(42).delay(1000).finally([&]() {
|
||||
elapsed = timer.elapsed();
|
||||
});
|
||||
|
||||
QCOMPARE(waitForValue(p, -1), 42);
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
|
||||
// Qt::CoarseTimer (default) Coarse timers try to
|
||||
// keep accuracy within 5% of the desired interval.
|
||||
// Require accuracy within 6% for passing the test.
|
||||
QVERIFY(elapsed >= 1000 * 0.94);
|
||||
QVERIFY(elapsed <= 1000 * 1.06);
|
||||
}
|
||||
|
||||
void tst_qpromise_delay::rejected()
|
||||
{
|
||||
QElapsedTimer timer;
|
||||
qint64 elapsed = -1;
|
||||
|
||||
timer.start();
|
||||
|
||||
auto p = QPromise<int>::reject(QString("foo")).delay(1000).finally([&]() {
|
||||
elapsed = timer.elapsed();
|
||||
});
|
||||
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QVERIFY(elapsed <= 10);
|
||||
}
|
4
tests/auto/qtpromise/qpromise/each/each.pro
Normal file
4
tests/auto/qtpromise/qpromise/each/each.pro
Normal file
@ -0,0 +1,4 @@
|
||||
TARGET = tst_qpromise_each
|
||||
SOURCES += $$PWD/tst_each.cpp
|
||||
|
||||
include(../../qtpromise.pri)
|
170
tests/auto/qtpromise/qpromise/each/tst_each.cpp
Normal file
170
tests/auto/qtpromise/qpromise/each/tst_each.cpp
Normal file
@ -0,0 +1,170 @@
|
||||
// Tests
|
||||
#include "../../shared/utils.h"
|
||||
|
||||
// QtPromise
|
||||
#include <QtPromise>
|
||||
|
||||
// Qt
|
||||
#include <QtTest>
|
||||
|
||||
using namespace QtPromise;
|
||||
|
||||
class tst_qpromise_each : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void emptySequence();
|
||||
void preserveValues();
|
||||
void ignoreResult();
|
||||
void delayedFulfilled();
|
||||
void delayedRejected();
|
||||
void functorThrows();
|
||||
void functorArguments();
|
||||
void sequenceTypes();
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_qpromise_each)
|
||||
#include "tst_each.moc"
|
||||
|
||||
namespace {
|
||||
|
||||
template <class Sequence>
|
||||
struct SequenceTester
|
||||
{
|
||||
static void exec()
|
||||
{
|
||||
QVector<int> values;
|
||||
auto p = QtPromise::qPromise(Sequence{42, 43, 44}).each([&](int v, int i) {
|
||||
values << i << v;
|
||||
}).each([&](int v, ...) {
|
||||
values << v;
|
||||
return QString("foo");
|
||||
}).each([&](int v, ...) {
|
||||
values << v + 1;
|
||||
return QPromise<QString>::resolve(QString("foo")).then([&](){
|
||||
values << -1;
|
||||
});
|
||||
}).each([&](int v, ...) {
|
||||
values << v + 2;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<Sequence>>::value));
|
||||
QCOMPARE(waitForValue(p, Sequence()), Sequence({42, 43, 44}));
|
||||
|
||||
QVector<int> expected{
|
||||
0, 42, 1, 43, 2, 44,
|
||||
42, 43, 44,
|
||||
43, 44, 45,
|
||||
-1, -1, -1,
|
||||
44, 45, 46
|
||||
};
|
||||
QCOMPARE(values, expected);
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void tst_qpromise_each::emptySequence()
|
||||
{
|
||||
QVector<int> values;
|
||||
auto p = QPromise<QVector<int>>::resolve({}).each([&](int v, ...) {
|
||||
values << v;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>());
|
||||
QCOMPARE(values, QVector<int>({}));
|
||||
}
|
||||
|
||||
void tst_qpromise_each::preserveValues()
|
||||
{
|
||||
QVector<int> values;
|
||||
auto p = QPromise<QVector<int>>::resolve({42, 43, 44}).each([&](int v, ...) {
|
||||
values << v + 1;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({42, 43, 44}));
|
||||
QCOMPARE(values, QVector<int>({43, 44, 45}));
|
||||
}
|
||||
|
||||
void tst_qpromise_each::ignoreResult()
|
||||
{
|
||||
QVector<int> values;
|
||||
auto p = QPromise<QVector<int>>::resolve({42, 43, 44}).each([&](int v, ...) {
|
||||
values << v + 1;
|
||||
return "Foo";
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({42, 43, 44}));
|
||||
QCOMPARE(values, QVector<int>({43, 44, 45}));
|
||||
}
|
||||
|
||||
void tst_qpromise_each::delayedFulfilled()
|
||||
{
|
||||
QMap<int, int> values;
|
||||
auto p = QPromise<QVector<int>>::resolve({42, 43, 44}).each([&](int v, int index) {
|
||||
return QPromise<void>::resolve().delay(250).then([=, &values]() {
|
||||
values[v] = index;
|
||||
return 42;
|
||||
});
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({42, 43, 44}));
|
||||
QMap<int, int> expected{{42, 0}, {43, 1}, {44, 2}};
|
||||
QCOMPARE(values, expected);
|
||||
}
|
||||
|
||||
void tst_qpromise_each::delayedRejected()
|
||||
{
|
||||
auto p = QPromise<QVector<int>>::resolve({42, 43, 44}).each([](int v, ...) {
|
||||
return QPromise<int>([&](
|
||||
const QPromiseResolve<int>& resolve,
|
||||
const QPromiseReject<int>& reject) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
if (v == 44) {
|
||||
reject(QString("foo"));
|
||||
}
|
||||
resolve(v);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
}
|
||||
|
||||
void tst_qpromise_each::functorThrows()
|
||||
{
|
||||
auto p = QPromise<QVector<int>>::resolve({42, 43, 44}).each([](int v, ...) {
|
||||
if (v == 44) {
|
||||
throw QString("foo");
|
||||
}
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
}
|
||||
|
||||
void tst_qpromise_each::functorArguments()
|
||||
{
|
||||
QVector<int> values;
|
||||
auto p = QPromise<QVector<int>>::resolve({42, 43, 44}).each([&](int v, int i) {
|
||||
values << i << v;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({42, 43, 44}));
|
||||
QCOMPARE(values, QVector<int>({0, 42, 1, 43, 2, 44}));
|
||||
}
|
||||
|
||||
void tst_qpromise_each::sequenceTypes()
|
||||
{
|
||||
SequenceTester<QList<int>>::exec();
|
||||
SequenceTester<QVector<int>>::exec();
|
||||
SequenceTester<std::list<int>>::exec();
|
||||
SequenceTester<std::vector<int>>::exec();
|
||||
}
|
4
tests/auto/qtpromise/qpromise/fail/fail.pro
Normal file
4
tests/auto/qtpromise/qpromise/fail/fail.pro
Normal file
@ -0,0 +1,4 @@
|
||||
TARGET = tst_qpromise_fail
|
||||
SOURCES += $$PWD/tst_fail.cpp
|
||||
|
||||
include(../../qtpromise.pri)
|
83
tests/auto/qtpromise/qpromise/fail/tst_fail.cpp
Normal file
83
tests/auto/qtpromise/qpromise/fail/tst_fail.cpp
Normal file
@ -0,0 +1,83 @@
|
||||
// Tests
|
||||
#include "../../shared/utils.h"
|
||||
|
||||
// QtPromise
|
||||
#include <QtPromise>
|
||||
|
||||
// Qt
|
||||
#include <QtTest>
|
||||
|
||||
using namespace QtPromise;
|
||||
|
||||
class tst_qpromise_fail : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void sameType();
|
||||
void baseClass();
|
||||
void catchAll();
|
||||
// TODO: sync / async
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_qpromise_fail)
|
||||
#include "tst_fail.moc"
|
||||
|
||||
void tst_qpromise_fail::sameType()
|
||||
{
|
||||
// http://en.cppreference.com/w/cpp/error/exception
|
||||
auto p = QPromise<int>::reject(std::out_of_range("foo"));
|
||||
|
||||
QString error;
|
||||
p.fail([&](const std::domain_error& e) {
|
||||
error += QString(e.what()) + "0";
|
||||
return -1;
|
||||
}).fail([&](const std::out_of_range& e) {
|
||||
error += QString(e.what()) + "1";
|
||||
return -1;
|
||||
}).fail([&](const std::exception& e) {
|
||||
error += QString(e.what()) + "2";
|
||||
return -1;
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(error, QString("foo1"));
|
||||
}
|
||||
|
||||
void tst_qpromise_fail::baseClass()
|
||||
{
|
||||
// http://en.cppreference.com/w/cpp/error/exception
|
||||
auto p = QPromise<int>::reject(std::out_of_range("foo"));
|
||||
|
||||
QString error;
|
||||
p.fail([&](const std::runtime_error& e) {
|
||||
error += QString(e.what()) + "0";
|
||||
return -1;
|
||||
}).fail([&](const std::logic_error& e) {
|
||||
error += QString(e.what()) + "1";
|
||||
return -1;
|
||||
}).fail([&](const std::exception& e) {
|
||||
error += QString(e.what()) + "2";
|
||||
return -1;
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(error, QString("foo1"));
|
||||
}
|
||||
|
||||
void tst_qpromise_fail::catchAll()
|
||||
{
|
||||
auto p = QPromise<int>::reject(std::out_of_range("foo"));
|
||||
|
||||
QString error;
|
||||
p.fail([&](const std::runtime_error& e) {
|
||||
error += QString(e.what()) + "0";
|
||||
return -1;
|
||||
}).fail([&]() {
|
||||
error += "bar";
|
||||
return -1;
|
||||
}).fail([&](const std::exception& e) {
|
||||
error += QString(e.what()) + "2";
|
||||
return -1;
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(error, QString("bar"));
|
||||
}
|
4
tests/auto/qtpromise/qpromise/filter/filter.pro
Normal file
4
tests/auto/qtpromise/qpromise/filter/filter.pro
Normal file
@ -0,0 +1,4 @@
|
||||
TARGET = tst_qpromise_filter
|
||||
SOURCES += $$PWD/tst_filter.cpp
|
||||
|
||||
include(../../qtpromise.pri)
|
152
tests/auto/qtpromise/qpromise/filter/tst_filter.cpp
Normal file
152
tests/auto/qtpromise/qpromise/filter/tst_filter.cpp
Normal file
@ -0,0 +1,152 @@
|
||||
// Tests
|
||||
#include "../../shared/utils.h"
|
||||
|
||||
// QtPromise
|
||||
#include <QtPromise>
|
||||
|
||||
// Qt
|
||||
#include <QtTest>
|
||||
|
||||
using namespace QtPromise;
|
||||
|
||||
class tst_qpromise_filter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void emptySequence();
|
||||
void filterValues();
|
||||
void delayedFulfilled();
|
||||
void delayedRejected();
|
||||
void functorThrows();
|
||||
void functorArguments();
|
||||
void preserveOrder();
|
||||
void sequenceTypes();
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_qpromise_filter)
|
||||
#include "tst_filter.moc"
|
||||
|
||||
namespace {
|
||||
|
||||
template <class Sequence>
|
||||
struct SequenceTester
|
||||
{
|
||||
static void exec()
|
||||
{
|
||||
auto p = QtPromise::qPromise(Sequence{
|
||||
42, 43, 44, 45, 46, 47, 48, 49, 50, 51
|
||||
}).filter([](int v, ...) {
|
||||
return v > 42 && v < 51;
|
||||
}).filter([](int, int i) {
|
||||
return QPromise<bool>::resolve(i % 2 == 0);
|
||||
}).filter([](int v, ...) {
|
||||
return v != 45;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<Sequence>>::value));
|
||||
QCOMPARE(waitForValue(p, Sequence()), Sequence({43, 47, 49}));
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
|
||||
void tst_qpromise_filter::emptySequence()
|
||||
{
|
||||
auto p = QPromise<QVector<int>>::resolve({}).filter([](int v, ...) {
|
||||
return v % 2 == 0;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>{});
|
||||
}
|
||||
|
||||
void tst_qpromise_filter::filterValues()
|
||||
{
|
||||
auto p = QPromise<QVector<int>>::resolve({42, 43, 44}).filter([](int v, ...) {
|
||||
return v % 2 == 0;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({42, 44}));
|
||||
}
|
||||
|
||||
void tst_qpromise_filter::delayedFulfilled()
|
||||
{
|
||||
auto p = QPromise<QVector<int>>::resolve({42, 43, 44}).filter([](int v, ...) {
|
||||
return QPromise<bool>([&](const QPromiseResolve<bool>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
resolve(v % 2 == 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({42, 44}));
|
||||
}
|
||||
|
||||
void tst_qpromise_filter::delayedRejected()
|
||||
{
|
||||
auto p = QPromise<QVector<int>>::resolve({42, 43, 44}).filter([](int v, ...) {
|
||||
return QPromise<bool>([&](
|
||||
const QPromiseResolve<bool>& resolve,
|
||||
const QPromiseReject<bool>& reject) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
if (v == 43) {
|
||||
reject(QString("foo"));
|
||||
}
|
||||
resolve(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
}
|
||||
|
||||
void tst_qpromise_filter::functorThrows()
|
||||
{
|
||||
auto p = QPromise<QVector<int>>::resolve({42, 43, 44}).filter([](int v, ...) {
|
||||
if (v == 43) {
|
||||
throw QString("foo");
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
}
|
||||
|
||||
void tst_qpromise_filter::functorArguments()
|
||||
{
|
||||
QMap<int, int> args;
|
||||
auto p = QPromise<QVector<int>>::resolve({42, 43, 44}).filter([&](int v, int i) {
|
||||
args[v] = i;
|
||||
return i % 2 == 0;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({42, 44}));
|
||||
QMap<int, int> expected{{42, 0}, {43, 1}, {44, 2}};
|
||||
QCOMPARE(args, expected);
|
||||
}
|
||||
|
||||
void tst_qpromise_filter::preserveOrder()
|
||||
{
|
||||
auto p = QPromise<QVector<int>>::resolve({250, 50, 100, 400, 300}).filter([](int v, ...) {
|
||||
return QPromise<bool>::resolve(v > 200).delay(v);
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({250, 400, 300}));
|
||||
}
|
||||
|
||||
void tst_qpromise_filter::sequenceTypes()
|
||||
{
|
||||
SequenceTester<QList<int>>::exec();
|
||||
SequenceTester<QVector<int>>::exec();
|
||||
SequenceTester<std::list<int>>::exec();
|
||||
SequenceTester<std::vector<int>>::exec();
|
||||
}
|
4
tests/auto/qtpromise/qpromise/finally/finally.pro
Normal file
4
tests/auto/qtpromise/qpromise/finally/finally.pro
Normal file
@ -0,0 +1,4 @@
|
||||
TARGET = tst_qpromise_finally
|
||||
SOURCES += $$PWD/tst_finally.cpp
|
||||
|
||||
include(../../qtpromise.pri)
|
204
tests/auto/qtpromise/qpromise/finally/tst_finally.cpp
Normal file
204
tests/auto/qtpromise/qpromise/finally/tst_finally.cpp
Normal file
@ -0,0 +1,204 @@
|
||||
// Tests
|
||||
#include "../../shared/utils.h"
|
||||
|
||||
// QtPromise
|
||||
#include <QtPromise>
|
||||
|
||||
// Qt
|
||||
#include <QtTest>
|
||||
|
||||
using namespace QtPromise;
|
||||
|
||||
class tst_qpromise_finally : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void fulfilledSync();
|
||||
void fulfilledSync_void();
|
||||
void fulfilledThrows();
|
||||
void fulfilledThrows_void();
|
||||
void fulfilledAsyncResolve();
|
||||
void fulfilledAsyncReject();
|
||||
void rejectedSync();
|
||||
void rejectedSync_void();
|
||||
void rejectedThrows();
|
||||
void rejectedThrows_void();
|
||||
void rejectedAsyncResolve();
|
||||
void rejectedAsyncReject();
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_qpromise_finally)
|
||||
#include "tst_finally.moc"
|
||||
|
||||
void tst_qpromise_finally::fulfilledSync()
|
||||
{
|
||||
int value = -1;
|
||||
auto p = QPromise<int>::resolve(42).finally([&]() {
|
||||
value = 8;
|
||||
return 16; // ignored!
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int>>::value));
|
||||
QCOMPARE(waitForValue(p, -1), 42);
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(value, 8);
|
||||
}
|
||||
|
||||
void tst_qpromise_finally::fulfilledSync_void()
|
||||
{
|
||||
int value = -1;
|
||||
auto p = QPromise<void>::resolve().finally([&]() {
|
||||
value = 8;
|
||||
return 16; // ignored!
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
|
||||
QCOMPARE(waitForValue(p, -1, 42), 42);
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(value, 8);
|
||||
}
|
||||
|
||||
void tst_qpromise_finally::fulfilledThrows()
|
||||
{
|
||||
auto p = QPromise<int>::resolve(42).finally([&]() {
|
||||
throw QString("bar");
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int>>::value));
|
||||
QCOMPARE(waitForError(p, QString()), QString("bar"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
}
|
||||
|
||||
void tst_qpromise_finally::fulfilledThrows_void()
|
||||
{
|
||||
auto p = QPromise<void>::resolve().finally([&]() {
|
||||
throw QString("bar");
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
|
||||
QCOMPARE(waitForError(p, QString()), QString("bar"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
}
|
||||
|
||||
void tst_qpromise_finally::fulfilledAsyncResolve()
|
||||
{
|
||||
QVector<int> values;
|
||||
auto p = QPromise<int>::resolve(42).finally([&]() {
|
||||
QPromise<int> p([&](const QPromiseResolve<int>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=, &values]() {
|
||||
values << 64;
|
||||
resolve(16); // ignored!
|
||||
});
|
||||
});
|
||||
|
||||
values << 8;
|
||||
return p;
|
||||
});
|
||||
|
||||
QCOMPARE(waitForValue(p, -1), 42);
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(values, QVector<int>({8, 64}));
|
||||
}
|
||||
|
||||
void tst_qpromise_finally::fulfilledAsyncReject()
|
||||
{
|
||||
auto p = QPromise<int>::resolve(42).finally([]() {
|
||||
return QPromise<int>([](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
reject(QString("bar"));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
QCOMPARE(waitForError(p, QString()), QString("bar"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
}
|
||||
|
||||
void tst_qpromise_finally::rejectedSync()
|
||||
{
|
||||
int value = -1;
|
||||
auto p = QPromise<int>::reject(QString("foo")).finally([&]() {
|
||||
value = 8;
|
||||
return 16; // ignored!
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int>>::value));
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(value, 8);
|
||||
}
|
||||
|
||||
void tst_qpromise_finally::rejectedSync_void()
|
||||
{
|
||||
int value = -1;
|
||||
auto p = QPromise<void>::reject(QString("foo")).finally([&]() {
|
||||
value = 8;
|
||||
return 16; // ignored!
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(value, 8);
|
||||
}
|
||||
|
||||
void tst_qpromise_finally::rejectedThrows()
|
||||
{
|
||||
auto p = QPromise<int>::reject(QString("foo")).finally([&]() {
|
||||
throw QString("bar");
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int>>::value));
|
||||
QCOMPARE(waitForError(p, QString()), QString("bar"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
}
|
||||
|
||||
void tst_qpromise_finally::rejectedThrows_void()
|
||||
{
|
||||
auto p = QPromise<void>::reject(QString("foo")).finally([&]() {
|
||||
throw QString("bar");
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
|
||||
QCOMPARE(waitForError(p, QString()), QString("bar"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
}
|
||||
|
||||
void tst_qpromise_finally::rejectedAsyncResolve()
|
||||
{
|
||||
QVector<int> values;
|
||||
auto p = QPromise<int>::reject(QString("foo")).finally([&]() {
|
||||
QPromise<int> p([&](const QPromiseResolve<int>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=, &values]() {
|
||||
values << 64;
|
||||
resolve(16); // ignored!
|
||||
});
|
||||
});
|
||||
|
||||
values << 8;
|
||||
return p;
|
||||
});
|
||||
|
||||
p.then([&](int r) {
|
||||
values << r;
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(values, QVector<int>({8, 64}));
|
||||
}
|
||||
|
||||
void tst_qpromise_finally::rejectedAsyncReject()
|
||||
{
|
||||
auto p = QPromise<int>::reject(QString("foo")).finally([]() {
|
||||
return QPromise<int>([](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
reject(QString("bar"));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
QCOMPARE(waitForError(p, QString()), QString("bar"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
}
|
4
tests/auto/qtpromise/qpromise/map/map.pro
Normal file
4
tests/auto/qtpromise/qpromise/map/map.pro
Normal file
@ -0,0 +1,4 @@
|
||||
TARGET = tst_qpromise_map
|
||||
SOURCES += $$PWD/tst_map.cpp
|
||||
|
||||
include(../../qtpromise.pri)
|
157
tests/auto/qtpromise/qpromise/map/tst_map.cpp
Normal file
157
tests/auto/qtpromise/qpromise/map/tst_map.cpp
Normal file
@ -0,0 +1,157 @@
|
||||
// Tests
|
||||
#include "../../shared/utils.h"
|
||||
|
||||
// QtPromise
|
||||
#include <QtPromise>
|
||||
|
||||
// Qt
|
||||
#include <QtTest>
|
||||
|
||||
using namespace QtPromise;
|
||||
|
||||
class tst_qpromise_map : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void emptySequence();
|
||||
void modifyValues();
|
||||
void convertValues();
|
||||
void delayedFulfilled();
|
||||
void delayedRejected();
|
||||
void functorThrows();
|
||||
void functorArguments();
|
||||
void preserveOrder();
|
||||
void sequenceTypes();
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_qpromise_map)
|
||||
#include "tst_map.moc"
|
||||
|
||||
namespace {
|
||||
|
||||
template <class Sequence>
|
||||
struct SequenceTester
|
||||
{
|
||||
static void exec()
|
||||
{
|
||||
auto p = QtPromise::qPromise(Sequence{42, 43, 44}).map([](int v, ...) {
|
||||
return QString::number(v + 1);
|
||||
}).map([](const QString& v, int i) {
|
||||
return QtPromise::qPromise(QString("%1:%2").arg(i).arg(v));
|
||||
}).map([](const QString& v, ...) {
|
||||
return QtPromise::qPromise((v + "!").toUtf8());
|
||||
}).map([](const QByteArray& v, ...) {
|
||||
return QString::fromUtf8(v);
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<QString>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<QString>()), QVector<QString>({"0:43!", "1:44!", "2:45!"}));
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void tst_qpromise_map::emptySequence()
|
||||
{
|
||||
auto p = QtPromise::qPromise(QVector<int>{}).map([](int v, ...) {
|
||||
return v + 1;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({}));
|
||||
}
|
||||
|
||||
void tst_qpromise_map::modifyValues()
|
||||
{
|
||||
auto p = QtPromise::qPromise(QVector<int>{42, 43, 44}).map([](int v, ...) {
|
||||
return v + 1;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({43, 44, 45}));
|
||||
}
|
||||
|
||||
void tst_qpromise_map::convertValues()
|
||||
{
|
||||
auto p = QtPromise::qPromise(QVector<int>{42, 43, 44}).map([](int v, ...) {
|
||||
return QString::number(v + 1);
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<QString>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<QString>()), QVector<QString>({"43", "44", "45"}));
|
||||
}
|
||||
|
||||
void tst_qpromise_map::delayedFulfilled()
|
||||
{
|
||||
auto p = QtPromise::qPromise(QVector<int>{42, 43, 44}).map([](int v, ...) {
|
||||
return QPromise<int>([&](const QPromiseResolve<int>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
resolve(v + 1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({43, 44, 45}));
|
||||
}
|
||||
|
||||
void tst_qpromise_map::delayedRejected()
|
||||
{
|
||||
auto p = QtPromise::qPromise(QVector<int>{42, 43, 44}).map([](int v, ...) {
|
||||
return QPromise<int>([&](
|
||||
const QPromiseResolve<int>& resolve,
|
||||
const QPromiseReject<int>& reject) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
if (v == 43) {
|
||||
reject(QString("foo"));
|
||||
}
|
||||
resolve(v);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
}
|
||||
|
||||
void tst_qpromise_map::functorThrows()
|
||||
{
|
||||
auto p = QtPromise::qPromise(QVector<int>{42, 43, 44}).map([](int v, ...) {
|
||||
if (v == 43) {
|
||||
throw QString("foo");
|
||||
}
|
||||
return v + 1;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
}
|
||||
|
||||
void tst_qpromise_map::functorArguments()
|
||||
{
|
||||
auto p1 = QtPromise::qPromise(QVector<int>{42, 42, 42}).map([](int v, int i) {
|
||||
return v * i;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p1), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p1, QVector<int>()), QVector<int>({0, 42, 84}));
|
||||
}
|
||||
|
||||
void tst_qpromise_map::preserveOrder()
|
||||
{
|
||||
auto p = QtPromise::qPromise(QVector<int>{250, 500, 100}).map([](int v, ...) {
|
||||
return QtPromise::qPromise(v + 1).delay(v);
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({251, 501, 101}));
|
||||
}
|
||||
|
||||
void tst_qpromise_map::sequenceTypes()
|
||||
{
|
||||
SequenceTester<QList<int>>::exec();
|
||||
SequenceTester<QVector<int>>::exec();
|
||||
SequenceTester<std::list<int>>::exec();
|
||||
SequenceTester<std::vector<int>>::exec();
|
||||
}
|
4
tests/auto/qtpromise/qpromise/operators/operators.pro
Normal file
4
tests/auto/qtpromise/qpromise/operators/operators.pro
Normal file
@ -0,0 +1,4 @@
|
||||
TARGET = tst_qpromise_operators
|
||||
SOURCES += $$PWD/tst_operators.cpp
|
||||
|
||||
include(../../qtpromise.pri)
|
230
tests/auto/qtpromise/qpromise/operators/tst_operators.cpp
Normal file
230
tests/auto/qtpromise/qpromise/operators/tst_operators.cpp
Normal file
@ -0,0 +1,230 @@
|
||||
// Tests
|
||||
#include "../../shared/utils.h"
|
||||
|
||||
// QtPromise
|
||||
#include <QtPromise>
|
||||
|
||||
// Qt
|
||||
#include <QtTest>
|
||||
|
||||
using namespace QtPromise;
|
||||
|
||||
class tst_qpromise_operators : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void move();
|
||||
void move_void();
|
||||
void copy();
|
||||
void copy_void();
|
||||
void equalTo();
|
||||
void equalTo_void();
|
||||
void notEqualTo();
|
||||
void notEqualTo_void();
|
||||
void chaining();
|
||||
void chaining_void();
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_qpromise_operators)
|
||||
#include "tst_operators.moc"
|
||||
|
||||
void tst_qpromise_operators::move()
|
||||
{
|
||||
auto p0 = QPromise<int>::resolve(42);
|
||||
|
||||
QCOMPARE(p0.isFulfilled(), true);
|
||||
QCOMPARE(waitForValue(p0, -1), 42);
|
||||
|
||||
p0 = QPromise<int>([](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
reject(QString("foo"));
|
||||
});
|
||||
});
|
||||
|
||||
QCOMPARE(p0.isPending(), true);
|
||||
QCOMPARE(waitForError(p0, QString()), QString("foo"));
|
||||
|
||||
p0 = QPromise<int>([](const QPromiseResolve<int>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
resolve(43);
|
||||
});
|
||||
});
|
||||
|
||||
QCOMPARE(p0.isPending(), true);
|
||||
QCOMPARE(waitForValue(p0, -1), 43);
|
||||
}
|
||||
|
||||
void tst_qpromise_operators::move_void()
|
||||
{
|
||||
auto p0 = QPromise<void>::resolve();
|
||||
|
||||
QCOMPARE(p0.isFulfilled(), true);
|
||||
QCOMPARE(waitForValue(p0, -1, 42), 42);
|
||||
|
||||
p0 = QPromise<void>([](const QPromiseResolve<void>&, const QPromiseReject<void>& reject) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
reject(QString("foo"));
|
||||
});
|
||||
});
|
||||
|
||||
QCOMPARE(p0.isPending(), true);
|
||||
QCOMPARE(waitForError(p0, QString()), QString("foo"));
|
||||
|
||||
p0 = QPromise<void>([](const QPromiseResolve<void>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
QCOMPARE(p0.isPending(), true);
|
||||
QCOMPARE(waitForValue(p0, -1, 43), 43);
|
||||
}
|
||||
|
||||
void tst_qpromise_operators::copy()
|
||||
{
|
||||
auto p0 = QPromise<int>([](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
reject(QString("foo"));
|
||||
});
|
||||
});
|
||||
|
||||
auto p1 = QPromise<int>([](const QPromiseResolve<int>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
resolve(42);
|
||||
});
|
||||
});
|
||||
|
||||
QCOMPARE(p0 == p1, false);
|
||||
QCOMPARE(p0.isPending(), true);
|
||||
QCOMPARE(p1.isPending(), true);
|
||||
|
||||
p0 = p1;
|
||||
|
||||
QCOMPARE(p0 == p1, true);
|
||||
QCOMPARE(p0.isPending(), true);
|
||||
QCOMPARE(p1.isPending(), true);
|
||||
QCOMPARE(waitForValue(p0, -1), 42);
|
||||
QCOMPARE(waitForValue(p1, -1), 42);
|
||||
}
|
||||
|
||||
void tst_qpromise_operators::copy_void()
|
||||
{
|
||||
auto p0 = QPromise<void>([](const QPromiseResolve<void>&, const QPromiseReject<void>& reject) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
reject(QString("foo"));
|
||||
});
|
||||
});
|
||||
|
||||
auto p1 = QPromise<void>([](const QPromiseResolve<void>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
QCOMPARE(p0 == p1, false);
|
||||
QCOMPARE(p0.isPending(), true);
|
||||
QCOMPARE(p1.isPending(), true);
|
||||
|
||||
p0 = p1;
|
||||
|
||||
QCOMPARE(p0 == p1, true);
|
||||
QCOMPARE(p0.isPending(), true);
|
||||
QCOMPARE(p1.isPending(), true);
|
||||
|
||||
p0.wait();
|
||||
|
||||
QCOMPARE(p0.isFulfilled(), true);
|
||||
QCOMPARE(p1.isFulfilled(), true);
|
||||
}
|
||||
|
||||
void tst_qpromise_operators::equalTo()
|
||||
{
|
||||
auto p0 = QPromise<int>::resolve(42);
|
||||
auto p1 = QPromise<int>::resolve(42);
|
||||
auto p2 = p1;
|
||||
auto p3(p2);
|
||||
|
||||
QCOMPARE(p0 == p1, false);
|
||||
QCOMPARE(p0 == p2, false);
|
||||
QCOMPARE(p0 == p3, false);
|
||||
QCOMPARE(p1 == p2, true);
|
||||
QCOMPARE(p1 == p3, true);
|
||||
QCOMPARE(p2 == p3, true);
|
||||
}
|
||||
|
||||
void tst_qpromise_operators::equalTo_void()
|
||||
{
|
||||
auto p0 = QPromise<void>::resolve();
|
||||
auto p1 = QPromise<void>::resolve();
|
||||
auto p2 = p1;
|
||||
auto p3(p2);
|
||||
|
||||
QCOMPARE(p0 == p1, false);
|
||||
QCOMPARE(p0 == p2, false);
|
||||
QCOMPARE(p0 == p3, false);
|
||||
QCOMPARE(p1 == p2, true);
|
||||
QCOMPARE(p1 == p3, true);
|
||||
QCOMPARE(p2 == p3, true);
|
||||
}
|
||||
|
||||
void tst_qpromise_operators::notEqualTo()
|
||||
{
|
||||
auto p0 = QPromise<int>::resolve(42);
|
||||
auto p1 = QPromise<int>::resolve(42);
|
||||
auto p2 = p1;
|
||||
auto p3(p2);
|
||||
|
||||
QCOMPARE(p0 != p1, true);
|
||||
QCOMPARE(p0 != p2, true);
|
||||
QCOMPARE(p0 != p3, true);
|
||||
QCOMPARE(p1 != p2, false);
|
||||
QCOMPARE(p1 != p3, false);
|
||||
QCOMPARE(p2 != p3, false);
|
||||
}
|
||||
|
||||
void tst_qpromise_operators::notEqualTo_void()
|
||||
{
|
||||
auto p0 = QPromise<void>::resolve();
|
||||
auto p1 = QPromise<void>::resolve();
|
||||
auto p2 = p1;
|
||||
auto p3(p2);
|
||||
|
||||
QCOMPARE(p0 != p1, true);
|
||||
QCOMPARE(p0 != p2, true);
|
||||
QCOMPARE(p0 != p3, true);
|
||||
QCOMPARE(p1 != p2, false);
|
||||
QCOMPARE(p1 != p3, false);
|
||||
QCOMPARE(p2 != p3, false);
|
||||
}
|
||||
|
||||
void tst_qpromise_operators::chaining()
|
||||
{
|
||||
auto p = QPromise<int>::resolve(1);
|
||||
for (int i=0; i<4; ++i) {
|
||||
p = p.then([](int res) {
|
||||
return QPromise<int>::resolve(res * 2);
|
||||
});
|
||||
}
|
||||
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, -1), 16);
|
||||
}
|
||||
|
||||
void tst_qpromise_operators::chaining_void()
|
||||
{
|
||||
QVector<int> values;
|
||||
|
||||
auto p = QPromise<void>::resolve();
|
||||
|
||||
for (int i=0; i<4; ++i) {
|
||||
p = p.then([i, &values]() {
|
||||
values.append(i * 2);
|
||||
return QPromise<void>::resolve();
|
||||
});
|
||||
}
|
||||
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, -1, 42), 42);
|
||||
QCOMPARE(values, QVector<int>({0, 2, 4, 6}));
|
||||
}
|
14
tests/auto/qtpromise/qpromise/qpromise.pro
Normal file
14
tests/auto/qtpromise/qpromise/qpromise.pro
Normal file
@ -0,0 +1,14 @@
|
||||
TEMPLATE = subdirs
|
||||
SUBDIRS += \
|
||||
construct \
|
||||
delay \
|
||||
each \
|
||||
fail \
|
||||
filter \
|
||||
finally \
|
||||
map \
|
||||
operators \
|
||||
tap \
|
||||
tapfail \
|
||||
then \
|
||||
timeout
|
4
tests/auto/qtpromise/qpromise/tap/tap.pro
Normal file
4
tests/auto/qtpromise/qpromise/tap/tap.pro
Normal file
@ -0,0 +1,4 @@
|
||||
TARGET = tst_qpromise_tap
|
||||
SOURCES += $$PWD/tst_tap.cpp
|
||||
|
||||
include(../../qtpromise.pri)
|
145
tests/auto/qtpromise/qpromise/tap/tst_tap.cpp
Normal file
145
tests/auto/qtpromise/qpromise/tap/tst_tap.cpp
Normal file
@ -0,0 +1,145 @@
|
||||
// Tests
|
||||
#include "../../shared/utils.h"
|
||||
|
||||
// QtPromise
|
||||
#include <QtPromise>
|
||||
|
||||
// Qt
|
||||
#include <QtTest>
|
||||
|
||||
using namespace QtPromise;
|
||||
|
||||
class tst_qpromise_tap : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void fulfilledSync();
|
||||
void fulfilledSync_void();
|
||||
void fulfilledThrows();
|
||||
void fulfilledThrows_void();
|
||||
void fulfilledAsyncResolve();
|
||||
void fulfilledAsyncReject();
|
||||
void rejectedSync();
|
||||
void rejectedSync_void();
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_qpromise_tap)
|
||||
#include "tst_tap.moc"
|
||||
|
||||
void tst_qpromise_tap::fulfilledSync()
|
||||
{
|
||||
int value = -1;
|
||||
auto p = QPromise<int>::resolve(42).tap([&](int res) {
|
||||
value = res + 1;
|
||||
return 8;
|
||||
});
|
||||
|
||||
QCOMPARE(waitForValue(p, 42), 42);
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(value, 43);
|
||||
}
|
||||
|
||||
void tst_qpromise_tap::fulfilledSync_void()
|
||||
{
|
||||
int value = -1;
|
||||
auto p = QPromise<void>::resolve().tap([&]() {
|
||||
value = 43;
|
||||
return 8;
|
||||
});
|
||||
|
||||
QCOMPARE(waitForValue(p, -1, 42), 42);
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(value, 43);
|
||||
}
|
||||
|
||||
void tst_qpromise_tap::fulfilledThrows()
|
||||
{
|
||||
auto p = QPromise<int>::resolve(42).tap([&](int) {
|
||||
throw QString("foo");
|
||||
});
|
||||
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
}
|
||||
|
||||
void tst_qpromise_tap::fulfilledThrows_void()
|
||||
{
|
||||
auto p = QPromise<void>::resolve().tap([&]() {
|
||||
throw QString("foo");
|
||||
});
|
||||
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
}
|
||||
|
||||
void tst_qpromise_tap::fulfilledAsyncResolve()
|
||||
{
|
||||
QVector<int> values;
|
||||
auto p = QPromise<int>::resolve(1).tap([&](int) {
|
||||
QPromise<int> p([&](const QPromiseResolve<int>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=, &values]() {
|
||||
values << 3;
|
||||
resolve(4); // ignored!
|
||||
});
|
||||
});
|
||||
|
||||
values << 2;
|
||||
return p;
|
||||
});
|
||||
|
||||
p.then([&](int r) {
|
||||
values << r;
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(values, QVector<int>({2, 3, 1}));
|
||||
}
|
||||
|
||||
void tst_qpromise_tap::fulfilledAsyncReject()
|
||||
{
|
||||
QVector<int> values;
|
||||
auto p = QPromise<int>::resolve(1).tap([&](int) {
|
||||
QPromise<int> p([&](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
|
||||
QtPromisePrivate::qtpromise_defer([=, &values]() {
|
||||
values << 3;
|
||||
reject(QString("foo"));
|
||||
});
|
||||
});
|
||||
|
||||
values << 2;
|
||||
return p;
|
||||
});
|
||||
|
||||
p.then([&](int r) {
|
||||
values << r;
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(values, QVector<int>({2, 3}));
|
||||
}
|
||||
|
||||
void tst_qpromise_tap::rejectedSync()
|
||||
{
|
||||
int value = -1;
|
||||
auto p = QPromise<int>::reject(QString("foo")).tap([&](int res) {
|
||||
value = res + 1;
|
||||
});
|
||||
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(value, -1);
|
||||
}
|
||||
|
||||
void tst_qpromise_tap::rejectedSync_void()
|
||||
{
|
||||
int value = -1;
|
||||
auto p = QPromise<void>::reject(QString("foo")).tap([&]() {
|
||||
value = 43;
|
||||
});
|
||||
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(value, -1);
|
||||
}
|
4
tests/auto/qtpromise/qpromise/tapfail/tapfail.pro
Normal file
4
tests/auto/qtpromise/qpromise/tapfail/tapfail.pro
Normal file
@ -0,0 +1,4 @@
|
||||
TARGET = tst_qpromise_tapfail
|
||||
SOURCES += $$PWD/tst_tapfail.cpp
|
||||
|
||||
include(../../qtpromise.pri)
|
159
tests/auto/qtpromise/qpromise/tapfail/tst_tapfail.cpp
Normal file
159
tests/auto/qtpromise/qpromise/tapfail/tst_tapfail.cpp
Normal 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}));
|
||||
}
|
4
tests/auto/qtpromise/qpromise/then/then.pro
Normal file
4
tests/auto/qtpromise/qpromise/then/then.pro
Normal file
@ -0,0 +1,4 @@
|
||||
TARGET = tst_qpromise_then
|
||||
SOURCES += $$PWD/tst_then.cpp
|
||||
|
||||
include(../../qtpromise.pri)
|
126
tests/auto/qtpromise/qpromise/then/tst_then.cpp
Normal file
126
tests/auto/qtpromise/qpromise/then/tst_then.cpp
Normal file
@ -0,0 +1,126 @@
|
||||
// Tests
|
||||
#include "../../shared/utils.h"
|
||||
|
||||
// QtPromise
|
||||
#include <QtPromise>
|
||||
|
||||
// Qt
|
||||
#include <QtTest>
|
||||
|
||||
using namespace QtPromise;
|
||||
|
||||
class tst_qpromise_then : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void resolveSync();
|
||||
void resolveAsync();
|
||||
void rejectSync();
|
||||
void rejectAsync();
|
||||
void skipResult();
|
||||
void noHandler();
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_qpromise_then)
|
||||
#include "tst_then.moc"
|
||||
|
||||
void tst_qpromise_then::resolveSync()
|
||||
{
|
||||
QVariantList values;
|
||||
|
||||
auto input = QPromise<int>::resolve(42);
|
||||
auto output = input.then([&](int res) {
|
||||
values << res;
|
||||
return QString::number(res+1);
|
||||
});
|
||||
|
||||
output.then([&](const QString& res) {
|
||||
values << res;
|
||||
}).then([&]() {
|
||||
values << 44;
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(values, QVariantList({42, QString("43"), 44}));
|
||||
QCOMPARE(input.isFulfilled(), true);
|
||||
QCOMPARE(output.isFulfilled(), true);
|
||||
}
|
||||
|
||||
void tst_qpromise_then::resolveAsync()
|
||||
{
|
||||
auto p = QPromise<int>::resolve(42).then([](int res) {
|
||||
return QPromise<QString>([=](const QPromiseResolve<QString>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
resolve(QString("foo%1").arg(res));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QString>>::value));
|
||||
QCOMPARE(waitForValue(p, QString()), QString("foo42"));
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
}
|
||||
|
||||
void tst_qpromise_then::rejectSync()
|
||||
{
|
||||
auto input = QPromise<int>::resolve(42);
|
||||
auto output = input.then([](int res) {
|
||||
throw QString("foo%1").arg(res);
|
||||
return 42;
|
||||
});
|
||||
|
||||
QString error;
|
||||
output.then([&](int res) {
|
||||
error += "bar" + QString::number(res);
|
||||
}).fail([&](const QString& err) {
|
||||
error += err;
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(error, QString("foo42"));
|
||||
QCOMPARE(input.isFulfilled(), true);
|
||||
QCOMPARE(output.isRejected(), true);
|
||||
}
|
||||
|
||||
void tst_qpromise_then::rejectAsync()
|
||||
{
|
||||
auto p = QPromise<int>::resolve(42).then([](int res) {
|
||||
return QPromise<void>([=](const QPromiseResolve<void>&, const QPromiseReject<void>& reject) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
reject(QString("foo%1").arg(res));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo42"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
}
|
||||
|
||||
void tst_qpromise_then::skipResult()
|
||||
{
|
||||
auto p = QPromise<int>::resolve(42);
|
||||
|
||||
int value = -1;
|
||||
p.then([&]() {
|
||||
value = 43;
|
||||
}).wait();
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int>>::value));
|
||||
QCOMPARE(value, 43);
|
||||
}
|
||||
|
||||
void tst_qpromise_then::noHandler()
|
||||
{
|
||||
{ // resolved
|
||||
auto p = QPromise<int>::resolve(42).then(nullptr);
|
||||
|
||||
QCOMPARE(waitForValue(p, -1), 42);
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
}
|
||||
{ // rejected
|
||||
auto p = QPromise<int>::reject(QString("foo")).then(nullptr);
|
||||
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
}
|
||||
}
|
4
tests/auto/qtpromise/qpromise/timeout/timeout.pro
Normal file
4
tests/auto/qtpromise/qpromise/timeout/timeout.pro
Normal file
@ -0,0 +1,4 @@
|
||||
TARGET = tst_qpromise_timeout
|
||||
SOURCES += $$PWD/tst_timeout.cpp
|
||||
|
||||
include(../../qtpromise.pri)
|
96
tests/auto/qtpromise/qpromise/timeout/tst_timeout.cpp
Normal file
96
tests/auto/qtpromise/qpromise/timeout/tst_timeout.cpp
Normal file
@ -0,0 +1,96 @@
|
||||
// Tests
|
||||
#include "../../shared/utils.h"
|
||||
|
||||
// QtPromise
|
||||
#include <QtPromise>
|
||||
|
||||
// Qt
|
||||
#include <QtTest>
|
||||
|
||||
using namespace QtPromise;
|
||||
|
||||
class tst_qpromise_timeout : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void fulfilled();
|
||||
void rejected();
|
||||
void timeout();
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_qpromise_timeout)
|
||||
#include "tst_timeout.moc"
|
||||
|
||||
void tst_qpromise_timeout::fulfilled()
|
||||
{
|
||||
QElapsedTimer timer;
|
||||
qint64 elapsed = -1;
|
||||
|
||||
timer.start();
|
||||
|
||||
auto p = QPromise<int>([](const QPromiseResolve<int>& resolve) {
|
||||
QTimer::singleShot(1000, [=]() {
|
||||
resolve(42);
|
||||
});
|
||||
}).timeout(2000).finally([&]() {
|
||||
elapsed = timer.elapsed();
|
||||
});
|
||||
|
||||
QCOMPARE(waitForValue(p, -1), 42);
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QVERIFY(elapsed < 2000);
|
||||
}
|
||||
|
||||
void tst_qpromise_timeout::rejected()
|
||||
{
|
||||
QElapsedTimer timer;
|
||||
qint64 elapsed = -1;
|
||||
|
||||
timer.start();
|
||||
|
||||
auto p = QPromise<int>([](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
|
||||
QTimer::singleShot(1000, [=]() {
|
||||
reject(QString("foo"));
|
||||
});
|
||||
}).timeout(2000).finally([&]() {
|
||||
elapsed = timer.elapsed();
|
||||
});
|
||||
|
||||
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QVERIFY(elapsed < 2000);
|
||||
}
|
||||
|
||||
void tst_qpromise_timeout::timeout()
|
||||
{
|
||||
QElapsedTimer timer;
|
||||
qint64 elapsed = -1;
|
||||
bool failed = false;
|
||||
|
||||
timer.start();
|
||||
|
||||
auto p = QPromise<int>([](const QPromiseResolve<int>& resolve) {
|
||||
QTimer::singleShot(4000, [=]() {
|
||||
resolve(42);
|
||||
});
|
||||
}).timeout(2000).finally([&]() {
|
||||
elapsed = timer.elapsed();
|
||||
});
|
||||
|
||||
p.fail([&](const QPromiseTimeoutException&) {
|
||||
failed = true;
|
||||
return -1;
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(waitForValue(p, -1), -1);
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(failed, true);
|
||||
|
||||
// Qt::CoarseTimer (default) Coarse timers try to
|
||||
// keep accuracy within 5% of the desired interval.
|
||||
// Require accuracy within 6% for passing the test.
|
||||
QVERIFY(elapsed >= 2000 * 0.94);
|
||||
QVERIFY(elapsed <= 2000 * 1.06);
|
||||
}
|
@ -1,12 +1,17 @@
|
||||
TEMPLATE = app
|
||||
CONFIG += testcase
|
||||
CONFIG += testcase warn_on
|
||||
QT += testlib
|
||||
QT -= gui
|
||||
|
||||
DEFINES += QT_DEPRECATED_WARNINGS
|
||||
|
||||
coverage: {
|
||||
gcc: {
|
||||
# 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)")
|
||||
QMAKE_CXXFLAGS += --coverage -O0 -g
|
||||
QMAKE_LFLAGS += --coverage -O0 -g
|
||||
@ -15,4 +20,7 @@ coverage: {
|
||||
}
|
||||
}
|
||||
|
||||
include(../../qtpromise.pri)
|
||||
HEADERS += \
|
||||
$$PWD/shared/utils.h
|
||||
|
||||
include(../../../qtpromise.pri)
|
8
tests/auto/qtpromise/qtpromise.pro
Normal file
8
tests/auto/qtpromise/qtpromise.pro
Normal file
@ -0,0 +1,8 @@
|
||||
TEMPLATE = subdirs
|
||||
SUBDIRS += \
|
||||
benchmark \
|
||||
future \
|
||||
helpers \
|
||||
qpromise \
|
||||
requirements \
|
||||
thread
|
@ -1,4 +1,4 @@
|
||||
TARGET = tst_requirements
|
||||
SOURCES += $$PWD/tst_requirements.cpp
|
||||
|
||||
include(../tests.pri)
|
||||
include(../qtpromise.pri)
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user