11 Commits

Author SHA1 Message Date
22faef4ac3 Bump version to 0.4.0 2018-09-09 21:27:33 +02:00
eebcb4f364 Migrate documentation to VuePress
GitBook development seems a bit stuck right now so let's switch to VuePress which, IMO, is more user friendly. Update the documentation to include the version number in which features were added and use custom container to display notes and warnings.
2018-09-09 10:55:07 +02:00
051fed5fbc Implement QPromise<Sequence<T>>::each(functor)
Call the given `functor` on each element in the promise value (i.e. `Sequence<T>`), then resolve to the original sequence unmodified. Also provide a static helper to directly filter values (`QtPromise::each(values, functor)`).
2018-09-07 16:50:29 +02:00
f610826ef0 Implement QtPromise::attempt(functor, args...)
Add a new helper that 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.

Also simplify PromiseDispatch which now calls the functor with a variable number of arguments (including none).
2018-05-31 09:02:51 +02:00
4fa7a37750 Implement QPromise<Sequence<T>>::filter(filterer)
Add a new method that iterates over all the promise values (i.e. `Sequence<T>`) and filters the sequence 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`. Also provide a static helper to directly filter values (`QtPromise::filter(values, filterer)`).
2018-05-30 18:52:53 +02:00
69c07855f4 Implement QPromise<Sequence<T>>::map(mapper) (#15)
Iterate over all the promise value (i.e. `Sequence<T>`) and map the sequence to another using the given `mapper` function. Also provide a static helper to directly map values (`QtPromise::map(values, mapper)`).
2018-05-26 11:40:51 +02:00
4cfe2e54f4 Move resolver related classes in a separate file. 2018-05-23 21:24:14 +02:00
54d88f16a3 Reorganize helpers unit tests 2018-05-23 21:24:00 +02:00
bdf3619469 Add AppVeyor configuration file for running MSVC tests (#19)
Qt 5.6/msvc2013 and Qt 5.9/msvc2013_64
2018-05-23 18:31:35 +02:00
8da467e9da Relax timing constraints when checking timeout in tst_timeout 2018-05-21 10:12:35 +02:00
efb6001b9d Upgrade TravisCI environment to Qt 5.6 and GCC 4.9 2018-05-18 11:32:01 +02:00
75 changed files with 2295 additions and 911 deletions

22
.appveyor.yml Normal file
View 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

View File

@ -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

View File

@ -1,18 +1,20 @@
<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
[![qpm](https://img.shields.io/github/release/simonbrunel/qtpromise.svg?style=flat-square&label=qpm&colorB=4CAF50)](https://www.qpm.io/packages/com.github.simonbrunel.qtpromise/index.html) [![Travis](https://img.shields.io/travis/simonbrunel/qtpromise/master.svg?style=flat-square)](https://travis-ci.org/simonbrunel/qtpromise) [![coverage](https://img.shields.io/codecov/c/github/simonbrunel/qtpromise.svg?style=flat-square)](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).
## Documentation
* [Getting Started](https://qtpromise.netlify.com/qtpromise/getting-started)
* [Thread-Safety](https://qtpromise.netlify.com/qtpromise/thread-safety)
* [QtConcurrent](https://qtpromise.netlify.com/qtpromise/qtconcurrent)
* [API Reference](https://qtpromise.netlify.com/qtpromise/api-reference)
* [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).

View File

@ -1,40 +0,0 @@
{
"title": "QtPromise",
"description": "Promises/A+ implementation for Qt/C++",
"author": "Simon Brunel",
"gitbook": "3.2.3",
"root": "docs",
"plugins": [
"-lunr",
"-search",
"search-plus",
"anchorjs",
"edit-link",
"expand-active-chapter",
"ga",
"github"
],
"pluginsConfig": {
"anchorjs": {
"icon": "#",
"placement": "left",
"visible": "always"
},
"edit-link": {
"base": "https://github.com/simonbrunel/qtpromise/edit/master/docs"
},
"ga": {
"token": "UA-113899811-1",
"configuration": "auto"
},
"github": {
"url": "https://github.com/simonbrunel/qtpromise"
},
"theme-default": {
"showLevel": false,
"styles": {
"website": "assets/style.css"
}
}
}
}

54
docs/.vuepress/config.js Normal file
View 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'
]
}
]
}
}

View File

@ -0,0 +1,2 @@
$accentColor = #23b223
$textColor = #404244

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -0,0 +1,4 @@
@import 'override.styl'
.content a code
color: $accentColor

View File

@ -3,13 +3,7 @@
# 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).
## QtPromise for C++
* [Getting Started](qtpromise/getting-started.md)
* [Thread-Safety](qtpromise/thread-safety.md)
* [QtConcurrent](qtpromise/qtconcurrent.md)
* [API Reference](qtpromise/api-reference.md)
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).

View File

@ -1,22 +0,0 @@
### QtPromise for C++
* [Getting Started](qtpromise/getting-started.md)
* [QtConcurrent](qtpromise/qtconcurrent.md)
* [Thread-Safety](qtpromise/thread-safety.md)
* [API Reference](qtpromise/api-reference.md)
* [QPromise](qtpromise/qpromise/constructor.md)
* [.delay](qtpromise/qpromise/delay.md)
* [.fail](qtpromise/qpromise/fail.md)
* [.finally](qtpromise/qpromise/finally.md)
* [.isFulfilled](qtpromise/qpromise/isfulfilled.md)
* [.isPending](qtpromise/qpromise/ispending.md)
* [.isRejected](qtpromise/qpromise/isrejected.md)
* [.tap](qtpromise/qpromise/tap.md)
* [.tapFail](qtpromise/qpromise/tapfail.md)
* [.then](qtpromise/qpromise/then.md)
* [.timeout](qtpromise/qpromise/timeout.md)
* [.wait](qtpromise/qpromise/wait.md)
* [::all (static)](qtpromise/qpromise/all.md)
* [::reject (static)](qtpromise/qpromise/reject.md)
* [::resolve (static)](qtpromise/qpromise/resolve.md)
* [qPromise](qtpromise/helpers/qpromise.md)
* [qPromiseAll](qtpromise/helpers/qpromiseall.md)

View File

@ -1,15 +0,0 @@
a.anchorjs-link {
color: rgba(65, 131, 196, 0.1);
font-weight: 400;
text-decoration: none;
transition: color 100ms ease-out;
z-index: 999;
}
a.anchorjs-link:hover {
color: rgba(65, 131, 196, 1);
}
sup {
font-size: 0.75em !important;
}

View File

@ -1,21 +1,24 @@
## QPromise
# API Reference
### Public Members
## 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)
### Public Static Members
## Static Functions
* [`[static] QPromise<T>::all`](qpromise/all.md)
* [`[static] QPromise<T>::reject`](qpromise/reject.md)
@ -23,5 +26,9 @@
## 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)

View File

@ -1,8 +1,10 @@
# 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
### qpm
Alternatively and **only** if your project relies on [qpm](https://www.qpm.io/), you can install QtPromise as follow:

View 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) {
// {...}
});
```

View 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)

View 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)

View 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)

View File

@ -1,4 +1,10 @@
## `qPromise`
---
title: qPromise
---
# qPromise
*Since: 0.1.0*
```
qPromise(T value) -> QPromise<R>

View File

@ -1,4 +1,10 @@
## `qPromiseAll`
---
title: qPromiseAll
---
# qPromiseAll
*Since: 0.1.0*
```
qPromiseAll(Sequence<QPromise<T>> promises) -> QPromise<QVector<T>>

View File

@ -1,4 +1,10 @@
## `[static] QPromise<T>::all`
---
title: ::all [static]
---
# QPromise::all [static]
*Since: 0.1.0*
```
[static] QPromise<T>::all(Sequence<QPromise<T>> promises) -> QPromise<QVector<T>>

View File

@ -1,6 +1,12 @@
## `QPromise<T>::QPromise`
---
title: constructor
---
```
# QPromise::QPromise
*Since: 0.1.0*
```cpp
QPromise<T>::QPromise(Function resolver)
```
@ -18,7 +24,9 @@ QPromise<int> promise([](const QPromiseResolve<int>& resolve, const QPromiseReje
});
```
> **Note:** `QPromise<void>` is specialized to not contain any value, meaning that the `resolve` callback takes no argument.
::: tip NOTE
`QPromise<void>` is specialized to not contain any value, meaning that the `resolve` callback takes no argument.
:::
**C++14**

View File

@ -1,6 +1,12 @@
## `QPromise<T>::delay`
---
title: .delay
---
```
# QPromise::delay
*Since: 0.2.0*
```cpp
QPromise<T>::delay(int msec) -> QPromise<T>
```

View 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)

View File

@ -1,6 +1,12 @@
## `QPromise<T>::fail`
---
title: .fail
---
```
# QPromise::fail
*Since: 0.1.0*
```cpp
QPromise<T>::fail(Function onRejected) -> QPromise<T>
```

View 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)

View File

@ -1,6 +1,12 @@
## `QPromise<T>::finally`
---
title: .finally
---
```
# QPromise::finally
*Since: 0.1.0*
```cpp
QPromise<T>::finally(Function handler) -> QPromise<T>
```

View File

@ -1,6 +1,12 @@
# `QPromise<T>::isFulfilled`
---
title: .isFulfilled
---
```
# QPromise::isFulfilled
*Since: 0.1.0*
```cpp
QPromise<T>::isFulfilled() -> bool
```

View File

@ -1,6 +1,12 @@
# `QPromise<T>::isPending`
---
title: .isPending
---
```
# QPromise::isPending
*Since: 0.1.0*
```cpp
QPromise<T>::isPending() -> bool
```

View File

@ -1,6 +1,12 @@
# `QPromise<T>::isRejected`
---
title: .isRejected
---
```
# QPromise::isRejected
*Since: 0.1.0*
```cpp
QPromise<T>::isRejected() -> bool
```

View 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)

View File

@ -1,6 +1,12 @@
## `[static] QPromise<T>::reject`
---
title: ::reject [static]
---
```
# QPromise::reject [static]
*Since: 0.1.0*
```cpp
[static] QPromise<T>::reject(any reason) -> QPromise<T>
```

View File

@ -1,4 +1,10 @@
## `[static] QPromise<T>::resolve`
---
title: ::resolve [static]
---
# QPromise::resolve [static]
*Since: 0.1.0*
```
[static] QPromise<T>::resolve(T value) -> QPromise<T>

View File

@ -1,6 +1,12 @@
## `QPromise<T>::tap`
---
title: .tap
---
```
# QPromise::tap
*Since: 0.2.0*
```cpp
QPromise<T>::tap(Function handler) -> QPromise<T>
```

View File

@ -1,6 +1,12 @@
## `QPromise<T>::tapFail`
---
title: .tapFail
---
```
# QPromise::tapFail
*Since: 0.4.0*
```cpp
QPromise<T>::tapFail(Function handler) -> QPromise<T>
```

View File

@ -1,6 +1,12 @@
## `QPromise<T>::then`
---
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>
```
@ -17,9 +23,11 @@ auto output = input.then([](int res) {
});
```
> **Note**: `onRejected` handler is optional, `output` will be rejected with the same reason as `input`.
> **Note**: it's recommended to use the [`fail`](fail.md) shorthand to handle errors.
::: 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:
@ -35,7 +43,11 @@ 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.
::: 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 = ...

View File

@ -1,6 +1,12 @@
## `QPromise<T>::timeout`
---
title: .timeout
---
```
# QPromise::timeout
*Since: 0.2.0*
```cpp
QPromise<T>::timeout(int msec, any error = QPromiseTimeoutException) -> QPromise<T>
```

View File

@ -1,6 +1,12 @@
## `QPromise<T>::wait`
---
title: .wait
---
```
# QPromise::wait
*Since: 0.1.0*
```cpp
QPromise<T>::wait() -> QPromise<T>
```

View File

@ -1,4 +1,4 @@
## QtConcurrent
# QtConcurrent
QtPromise integrates with [QtConcurrent](https://doc.qt.io/qt-5/qtconcurrent-index.html) to make easy chaining QFuture with QPromise.

View File

@ -1,5 +1,7 @@
## Thread-Safety
# 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.
> **Note:** 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.
::: 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.
:::

View File

@ -10,9 +10,9 @@
"url": "https://github.com/simonbrunel/qtpromise.git"
},
"version": {
"label": "0.3.0"
"label": "0.4.0"
},
"license": "MIT",
"pri_filename": "qtpromise.pri",
"webpage": "https://github.com/simonbrunel/qtpromise"
"webpage": "https://qtpromise.netlify.com"
}

View File

@ -5,6 +5,7 @@
#include "qpromise_p.h"
#include "qpromiseerror.h"
#include "qpromiseglobal.h"
#include "qpromiseresolver.h"
// Qt
#include <QExplicitlySharedDataPointer>
@ -23,9 +24,6 @@ public:
template <typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count != 1, int>::type = 0>
inline QPromiseBase(F resolver);
template <typename U>
inline QPromiseBase(const QPromise<U>& other);
QPromiseBase(const QPromiseBase<T>& other): m_d(other.m_d) {}
QPromiseBase(const QPromise<T>& other): m_d(other.m_d) {}
QPromiseBase(QPromiseBase<T>&& other) Q_DECL_NOEXCEPT { swap(other); }
@ -90,6 +88,16 @@ 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
template <template <typename, typename...> class Sequence = QVector, typename ...Args>
inline static QPromise<QVector<T>> all(const Sequence<QPromise<T>, Args...>& promises);
@ -97,9 +105,6 @@ public: // STATIC
inline static QPromise<T> resolve(const T& value);
inline static QPromise<T> resolve(T&& value);
template <typename U>
operator QPromise<U>();
private:
friend class QPromiseBase<T>;
};
@ -111,9 +116,6 @@ public:
template <typename F>
QPromise(F&& resolver): QPromiseBase<void>(std::forward<F>(resolver)) { }
template <typename T>
QPromise(const QPromise<T>& other);
public: // STATIC
template <template <typename, typename...> class Sequence = QVector, typename ...Args>
inline static QPromise<void> all(const Sequence<QPromise<void>, Args...>& promises);

View File

@ -1,3 +1,6 @@
#include "qpromise.h"
#include "qpromisehelpers.h"
// Qt
#include <QCoreApplication>
#include <QSharedPointer>
@ -5,47 +8,6 @@
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;
};
template <typename T>
template <typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count == 1, int>::type>
inline QPromiseBase<T>::QPromiseBase(F callback)
@ -74,19 +36,6 @@ inline QPromiseBase<T>::QPromiseBase(F callback)
}
}
template <typename T>
template <typename U>
inline QPromiseBase<T>::QPromiseBase(const QPromise<U>& other)
: m_d(new QtPromisePrivate::PromiseData<T>())
{
using namespace QtPromisePrivate;
QPromiseResolve<T> resolve(*this);
QPromiseReject<T> reject(*this);
PromiseFulfill<QPromise<U> >::call(other, PromiseCast<U, T>::apply(resolve), reject);
}
template <typename T>
template <typename TFulfilled, typename TRejected>
inline typename QtPromisePrivate::PromiseHandler<T, TFulfilled>::Promise
@ -206,6 +155,48 @@ inline QPromise<T> QPromiseBase<T>::reject(E&& error)
});
}
template <typename T>
template <typename Functor>
inline QPromise<T> QPromise<T>::each(Functor fn)
{
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)
@ -257,21 +248,6 @@ inline QPromise<T> QPromise<T>::resolve(T&& value)
});
}
template <typename T>
template <typename U>
QPromise<T>::operator QPromise<U>()
{
return QPromise<U>::resolve(U());
}
template <typename T>
QPromise<void>::QPromise(const QPromise<T>& other)
: QPromiseBase<void>([&](const QPromiseResolve<void>& resolve, const QPromiseReject<void>& reject) {
QtPromisePrivate::PromiseFulfill<QPromise<T> >::call(other, resolve, reject);
})
{
}
template <template <typename, typename...> class Sequence, typename ...Args>
inline QPromise<void> QPromise<void>::all(const Sequence<QPromise<void>, Args...>& promises)
{

View File

@ -119,11 +119,20 @@ 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
{
template <typename TResolve, typename TReject>
static void call(T&& value, const TResolve& resolve, const TReject&)
static void call(
T&& value,
const QtPromise::QPromiseResolve<T>& resolve,
const QtPromise::QPromiseReject<T>&)
{
resolve(std::move(value));
}
@ -132,11 +141,10 @@ struct PromiseFulfill
template <typename T>
struct PromiseFulfill<QtPromise::QPromise<T>>
{
template <typename TResolve, typename TReject>
static void call(
const QtPromise::QPromise<T>& promise,
const TResolve& resolve,
const TReject& reject)
const QtPromise::QPromiseResolve<T>& resolve,
const QtPromise::QPromiseReject<T>& reject)
{
if (promise.isFulfilled()) {
resolve(promise.m_d->value());
@ -155,9 +163,9 @@ struct PromiseFulfill<QtPromise::QPromise<T>>
template <>
struct PromiseFulfill<QtPromise::QPromise<void>>
{
template <typename TResolve, typename TReject>
template <typename TPromise, typename TResolve, typename TReject>
static void call(
const QtPromise::QPromise<void>& promise,
const TPromise& promise,
const TResolve& resolve,
const TReject& reject)
{
@ -175,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());
}
@ -227,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());
@ -247,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(
@ -256,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);
};
}
};
@ -265,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(
@ -274,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);
};
}
};
@ -283,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(
@ -292,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);
};
}
};
@ -350,7 +322,7 @@ struct PromiseCatcher
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());
}
@ -373,7 +345,7 @@ struct PromiseCatcher<T, THandler, void>
try {
error.rethrow();
} catch (...) {
PromiseDispatch<void, ResType>::call(handler, resolve, reject);
PromiseDispatch<ResType>::call(resolve, reject, handler);
}
};
}
@ -396,66 +368,17 @@ struct PromiseCatcher<T, std::nullptr_t, void>
}
};
template <typename T, typename U>
struct PromiseCast
{
template <typename F>
static auto apply(const F& resolve)
{
return [=](const QSharedPointer<T>& value) {
resolve(static_cast<T>(*value));
};
}
};
template <typename T, typename F>
struct PromiseMapper
{ };
template <typename T>
struct PromiseCast<T, void>
template <typename T, typename F, template <typename, typename...> class Sequence, typename ...Args>
struct PromiseMapper<Sequence<T, Args...>, F>
{
template <typename F>
static auto apply(const F& resolve)
{
return [=](const QSharedPointer<T>&) {
resolve();
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>
struct PromiseCast<T, QVariant>
{
static QtPromise::QPromise<QVariant> cast(
const QtPromise::QPromiseBase<T>& input)
{
return input.then([](const T& res) {
return QVariant::fromValue(res);
});
}
};
template <typename U>
struct PromiseCast<QVariant, U>
{
static QtPromise::QPromise<U> cast(
const QtPromise::QPromiseBase<QVariant>& input)
{
return input.then([](const QVariant& res) {
return res.value<U>();
});
}
};
template <>
struct PromiseCast<void, QVariant>
{
static QtPromise::QPromise<void> cast(
const QtPromise::QPromiseBase<QVariant>& input)
{
return input.then([]() {
});
}
};
*/
template <typename T> class PromiseData;
@ -622,68 +545,6 @@ protected:
}
};
template <typename T>
class PromiseResolver
{
public:
PromiseResolver(QtPromise::QPromise<T> promise)
: m_d(new Data())
{
m_d->promise = new QtPromise::QPromise<T>(std::move(promise));
}
template <typename E>
void reject(E&& error)
{
auto promise = m_d->promise;
if (promise) {
Q_ASSERT(promise->isPending());
promise->m_d->reject(std::forward<E>(error));
promise->m_d->dispatch();
release();
}
}
template <typename V>
void resolve(V&& value)
{
auto promise = m_d->promise;
if (promise) {
Q_ASSERT(promise->isPending());
promise->m_d->resolve(std::forward<V>(value));
promise->m_d->dispatch();
release();
}
}
void resolve()
{
auto promise = m_d->promise;
if (promise) {
Q_ASSERT(promise->isPending());
promise->m_d->resolve();
promise->m_d->dispatch();
release();
}
}
private:
struct Data : public QSharedData
{
QtPromise::QPromise<T>* promise = nullptr;
};
QExplicitlySharedDataPointer<Data> m_d;
void release()
{
Q_ASSERT(m_d->promise);
Q_ASSERT(!m_d->promise->isPending());
delete m_d->promise;
m_d->promise = nullptr;
}
};
} // namespace QtPromise
#endif // ifndef QTPROMISE_QPROMISE_P_H
#endif // ifndef QTPROMISE_QPROMISE_H

View File

@ -38,6 +38,83 @@ static inline QPromise<void> qPromiseAll(const Sequence<QPromise<void>, Args...>
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

View 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

View File

@ -5,4 +5,5 @@ HEADERS += \
$$PWD/qpromiseerror.h \
$$PWD/qpromisefuture.h \
$$PWD/qpromiseglobal.h \
$$PWD/qpromisehelpers.h
$$PWD/qpromisehelpers.h \
$$PWD/qpromiseresolver.h

View File

@ -1,4 +1,4 @@
TARGET = tst_qpromise_all
TARGET = tst_helpers_all
SOURCES += $$PWD/tst_all.cpp
include(../../qtpromise.pri)

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

View File

@ -0,0 +1,5 @@
QT += concurrent
TARGET = tst_helpers_attempt
SOURCES += $$PWD/tst_attempt.cpp
include(../../qtpromise.pri)

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

View File

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

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

View File

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

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

View File

@ -1,4 +1,9 @@
TARGET = tst_helpers
SOURCES += $$PWD/tst_helpers.cpp
include(../qtpromise.pri)
TEMPLATE = subdirs
SUBDIRS += \
all \
attempt \
each \
filter \
map \
reject \
resolve

View File

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

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

View File

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

View File

@ -12,7 +12,7 @@
using namespace QtPromise;
class tst_qpromise_reject : public QObject
class tst_helpers_reject : public QObject
{
Q_OBJECT
@ -22,10 +22,10 @@ private Q_SLOTS:
void rejectWithStdSharedPtr();
};
QTEST_MAIN(tst_qpromise_reject)
QTEST_MAIN(tst_helpers_reject)
#include "tst_reject.moc"
void tst_qpromise_reject::rejectWithValue()
void tst_helpers_reject::rejectWithValue()
{
auto p = QPromise<int>::reject(42);
@ -34,7 +34,7 @@ void tst_qpromise_reject::rejectWithValue()
}
// https://github.com/simonbrunel/qtpromise/issues/6
void tst_qpromise_reject::rejectWithQSharedPtr()
void tst_helpers_reject::rejectWithQSharedPtr()
{
QWeakPointer<int> wptr;
@ -54,7 +54,7 @@ void tst_qpromise_reject::rejectWithQSharedPtr()
}
// https://github.com/simonbrunel/qtpromise/issues/6
void tst_qpromise_reject::rejectWithStdSharedPtr()
void tst_helpers_reject::rejectWithStdSharedPtr()
{
std::weak_ptr<int> wptr;

View File

@ -1,4 +1,4 @@
TARGET = tst_qpromise_resolve
TARGET = tst_helpers_resolve
SOURCES += $$PWD/tst_resolve.cpp
include(../../qtpromise.pri)

View File

@ -12,41 +12,80 @@
using namespace QtPromise;
class tst_qpromise_resolve : public QObject
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_qpromise_resolve)
QTEST_MAIN(tst_helpers_resolve)
#include "tst_resolve.moc"
void tst_qpromise_resolve::resolveWithValue()
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_qpromise_resolve::resolveWithNoValue()
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_qpromise_resolve::resolveWithQSharedPtr()
void tst_helpers_resolve::resolveWithQSharedPtr()
{
QWeakPointer<int> wptr;
@ -66,7 +105,7 @@ void tst_qpromise_resolve::resolveWithQSharedPtr()
}
// https://github.com/simonbrunel/qtpromise/issues/6
void tst_qpromise_resolve::resolveWithStdSharedPtr()
void tst_helpers_resolve::resolveWithStdSharedPtr()
{
std::weak_ptr<int> wptr;

View File

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

View File

@ -1,103 +0,0 @@
// Tests
#include "../../shared/utils.h"
// QtPromise
#include <QtPromise>
// Qt
#include <QtTest>
using namespace QtPromise;
class tst_qpromise_all : public QObject
{
Q_OBJECT
private Q_SLOTS:
void qList();
//void qVector();
void stdList();
void stdVector();
};
QTEST_MAIN(tst_qpromise_all)
#include "tst_all.moc"
namespace {
template <class Sequence>
struct SequenceTester
{
};
template <template <typename, typename...> class Sequence, typename ...Args>
struct SequenceTester<Sequence<QPromise<int>, Args...>>
{
static void exec()
{
Sequence<QPromise<int>, Args...> promises{
QPromise<int>::resolve(42),
QPromise<int>::resolve(43),
QPromise<int>::resolve(44)
};
promises.push_back(QPromise<int>::resolve(45));
promises.insert(++promises.begin(), QPromise<int>::resolve(46));
promises.pop_back();
auto p = QPromise<int>::all(promises);
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({42, 46, 43, 44}));
}
};
template <template <typename, typename...> class Sequence, typename ...Args>
struct SequenceTester<Sequence<QPromise<void>, Args...>>
{
static void exec()
{
Sequence<QPromise<void>, Args...> promises{
QPromise<void>::resolve(),
QPromise<void>::resolve(),
QPromise<void>::resolve()
};
promises.push_back(QPromise<void>::resolve());
promises.insert(++promises.begin(), QPromise<void>::resolve());
promises.pop_back();
auto p = QPromise<void>::all(promises);
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
QCOMPARE(waitForValue(p, -1, 42), 42);
}
};
} // anonymous namespace
void tst_qpromise_all::qList()
{
SequenceTester<QList<QPromise<int>>>::exec();
SequenceTester<QList<QPromise<void>>>::exec();
}
// QVector::push_back/append isn't supported since it requires a default
// constructor (see https://github.com/simonbrunel/qtpromise/issues/3)
//void tst_qpromise_all::qVector()
//{
// SequenceTester<QVector<QPromise<int>>>::exec();
// SequenceTester<QVector<QPromise<void>>>::exec();
//}
void tst_qpromise_all::stdList()
{
SequenceTester<std::list<QPromise<int>>>::exec();
SequenceTester<std::list<QPromise<void>>>::exec();
}
void tst_qpromise_all::stdVector()
{
SequenceTester<std::vector<QPromise<int>>>::exec();
SequenceTester<std::vector<QPromise<void>>>::exec();
}

View File

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

View File

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

View File

@ -34,8 +34,12 @@ void tst_qpromise_delay::fulfilled()
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.
// 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()
@ -51,5 +55,5 @@ void tst_qpromise_delay::rejected()
QCOMPARE(waitForError(p, QString()), QString("foo"));
QCOMPARE(p.isRejected(), true);
QVERIFY(elapsed < 5);
QVERIFY(elapsed <= 10);
}

View File

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

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

View File

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

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

View File

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

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

View File

@ -1,14 +1,13 @@
TEMPLATE = subdirs
SUBDIRS += \
all \
cast \
construct \
delay \
each \
fail \
filter \
finally \
map \
operators \
reject \
resolve \
tap \
tapfail \
then \

View File

@ -87,6 +87,10 @@ void tst_qpromise_timeout::timeout()
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.
// 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);
}