24 Commits

Author SHA1 Message Date
67837827b1 Bump version to 0.5.0 2019-03-31 22:14:45 +02:00
e3f0f054af Implement QPromise<Sequence<T>>::reduce(reducer, initialValue)
Iterates over all the promise values (i.e. `Sequence<T>`) and reduces the sequence to a single value using the given `reducer` function and an optional `initialValue`. Also provide a static helper to directly reduce values (`QtPromise::reduce(values, reducer, initialValue)`).
2019-03-31 15:16:08 +02:00
cbf4cc7867 Rename qPromiseAll() helper to QtPromise::all()
For consistency with other helpers, deprecate `qPromiseAll()` in favor of `QtPromise::all()`.
2019-03-25 14:45:51 +01:00
963ec621e1 Rename qPromise() helper to QtPromise::resolve()
For consistency with other helpers, deprecate `qPromise()` in favor of `QtPromise::resolve()` but also add support for calling this helper with lvalue. Add extra unit tests to make sure that rvalue is not copied.
2019-03-25 14:45:41 +01:00
1f30224578 Add support for creating promises from Qt signals (#25)
Introduce a new `QtPromise::connect()` helper that allows to create a promise resolved from a single signal and optionally, rejected by another one (from a different object or not). The promise type is determined by the type of the first signal argument (other arguments are currently ignored). A `QPromise<void>` is returned if the resolve signal doesn't provide any argument.

If the rejection is emitted before the promise is resolved, the promise will be rejected with the value of the first argument (other arguments being ignored). If the rejection signal doesn't provide any argument, the promise will be rejected with `QPromiseUndefinedException` if the signal is emitted. Additionally, the promise will be automatically rejected with `QPromiseContextException` if the source object is destroyed before the promise is resolved.
2019-03-04 14:02:32 +01:00
47b90fb532 Fix Gcov warning 'version 409*, prefer version 408*' 2019-03-01 17:47:32 +01:00
63acdfaab9 Make docs formatting consistent (no explicit word wrap) 2019-03-01 17:41:32 +01:00
700098ef7b Reorganize exceptions and add unit tests 2019-03-01 17:41:32 +01:00
6110cd40d3 Test LTS and latest Qt versions in AppVeyor 2019-02-21 12:07:21 +01:00
a6d883acbd Update LICENSE year and cleanup a few comments 2019-02-18 20:30:41 +01:00
9119cc72f6 Add documentation for built-in exceptions 2019-02-01 16:06:29 +01:00
fa5a4192ff Allow undefined rejection reason
While not recommended because it makes tracking errors more difficult, it's now possible to reject a promise without explicit reason, in which case, a built-in `QPromiseUndefinedException` is thrown. This is done in anticipation of handling rejection signals without argument.
2019-01-31 19:03:57 +01:00
16229fc2c9 Integrate DocSearch (Algolia)
https://github.com/algolia/docsearch-configs/blob/master/configs/qtpromise.json
https://community.algolia.com/docsearch/
2018-09-10 12:36:31 +02:00
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
128 changed files with 5340 additions and 982 deletions

27
.appveyor.yml Normal file
View File

@ -0,0 +1,27 @@
image: Visual Studio 2015
init:
- cmd: call "C:\Program Files (x86)\Microsoft Visual Studio "%VSVER%".0\VC\vcvarsall.bat" %ARCH%
- cmd: qmake --version
# https://www.appveyor.com/docs/windows-images-software/#qt
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
- QTDIR: C:\Qt\latest\msvc2015_64
VSVER: 14
ARCH: x64
build_script:
- cmd: qmake qtpromise.pro
- cmd: nmake
test_script:
- cmd: nmake check

View File

@ -4,12 +4,17 @@ 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 90
- sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.9 90
- sudo update-alternatives --install /usr/bin/gcov gcov /usr/bin/gcov-4.9 90
- 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/
@ -18,8 +23,8 @@ install:
before_script:
- qmake --version
- lcov --version
- gcc --version
- lcov --version && gcov --version
- gcc --version && g++ --version
script:
- qmake qtpromise.pro CONFIG+=coverage

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2017 Simon Brunel
Copyright (c) 2019 Simon Brunel
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@ -1,18 +1,21 @@
<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)
* [Qt Concurrent](https://qtpromise.netlify.com/qtpromise/qtconcurrent.html)
* [Qt Signals](https://qtpromise.netlify.com/qtpromise/qtsignals.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"
}
}
}
}

70
docs/.vuepress/config.js Normal file
View File

@ -0,0 +1,70 @@
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',
algolia: {
apiKey: '0e6e9cccb8c2c360a5543e28c4e31cb8',
indexName: 'qtpromise'
},
sidebar: [
'qtpromise/getting-started',
'qtpromise/qtconcurrent',
'qtpromise/qtsignals',
'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/reduce',
'qtpromise/qpromise/tap',
'qtpromise/qpromise/tapfail',
'qtpromise/qpromise/then',
'qtpromise/qpromise/timeout',
'qtpromise/qpromise/wait',
'qtpromise/qpromise/reject.md',
'qtpromise/qpromise/resolve.md'
]
},
{
title: 'Helpers',
children: [
'qtpromise/helpers/all',
'qtpromise/helpers/attempt',
'qtpromise/helpers/connect',
'qtpromise/helpers/each',
'qtpromise/helpers/filter',
'qtpromise/helpers/map',
'qtpromise/helpers/reduce',
'qtpromise/helpers/resolve'
]
},
{
title: 'Exceptions',
children: [
'qtpromise/exceptions/canceled',
'qtpromise/exceptions/context',
'qtpromise/exceptions/timeout',
'qtpromise/exceptions/undefined'
]
}
]
}
}

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,27 +1,49 @@
## 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>::reduce`](qpromise/reduce.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)
* [`[static] QPromise<T>::resolve`](qpromise/resolve.md)
## Helpers
* [`qPromise`](helpers/qpromise.md)
* [`qPromiseAll`](helpers/qpromiseall.md)
* [`QtPromise::all`](helpers/all.md)
* [`QtPromise::attempt`](helpers/attempt.md)
* [`QtPromise::connect`](helpers/connect.md)
* [`QtPromise::each`](helpers/each.md)
* [`QtPromise::filter`](helpers/filter.md)
* [`QtPromise::map`](helpers/map.md)
* [`QtPromise::reduce`](helpers/reduce.md)
* [`QtPromise::resolve`](helpers/resolve.md)
## Exceptions
* [`QPromiseCanceledException`](exceptions/canceled.md)
* [`QPromiseContextException`](exceptions/context.md)
* [`QPromiseTimeoutException`](exceptions/timeout.md)
* [`QPromiseUndefinedException`](exceptions/undefined.md)
## Deprecations
* `[static] QPromise<T>::all`: use [`QtPromise::all`](helpers/all.md) instead (since 0.5.0)
* `QtPromise::qPromise`: use [`QtPromise::resolve`](helpers/resolve.md) instead (since 0.5.0)
* `QtPromise::qPromiseAll`: use [`QtPromise::all`](helpers/all.md) instead (since 0.5.0)

View File

@ -0,0 +1,20 @@
---
title: QPromiseCanceledException
---
# QPromiseCanceledException
*Since: 0.1.0*
This exception is thrown for promise created from a [`QFuture`](../qtconcurrent.md) which has been canceled (e.g. using [`QFuture::cancel()`](http://doc.qt.io/qt-5/qfuture.html#cancel)), for example:
```cpp
auto output = QtPromise::resolve(future)
.fail([](const QPromiseCanceledException&) {
// `future` has been canceled!
});
```
::: tip NOTE
QtPromise doesn't support promise cancelation (yet?)
:::

View File

@ -0,0 +1,17 @@
---
title: QPromiseContextException
---
# QPromiseContextException
*Since: 0.5.0*
When a promise is created using [`QtPromise::connect()`](../helpers/connect.md), this exception is thrown when the `sender` object is destroyed, for example:
```cpp
auto promise = QtPromise::connect(sender, &Object::finished, &Object::error);
promise.fail([](const QPromiseContextException&) {
// 'sender' has been destroyed.
})
```

View File

@ -0,0 +1,17 @@
---
title: QPromiseTimeoutException
---
# QPromiseTimeoutException
*Since: 0.2.0*
This is the default exception thrown when reaching the time limit when using the [`QPromise::timeout()`](../qpromise/timeout.md) method, for example:
```cpp
QPromise<int> input = {...}
auto output = input.timeout(2000)
.fail([](const QPromiseTimeoutException& e) {
// operation timed out after 2s!
});
```

View File

@ -0,0 +1,25 @@
---
title: QPromiseUndefinedException
---
# QPromiseUndefinedException
*Since: 0.5.0*
This exception is thrown when rejecting a promise with no explicit reason, for example:
```cpp
QPromise<int> promise([](const QPromiseResolve<int>& resolve, const QPromiseReject<int>& reject) {
async_method([=](bool success, int result) {
if (success) {
resolve(result);
} else {
reject();
}
});
});
promise.fail([](const QPromiseUndefinedException&) {
// promise rejected without reason!
})
```

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:
@ -54,7 +56,7 @@ The following method `uncompress` data in a separate thread and returns a [promi
```cpp
QPromise<Entries> uncompress(const QByteArray& data)
{
return qPromise(QtConcurrent::run([](const QByteArray& data) {
return QtPromise::resolve(QtConcurrent::run([](const QByteArray& data) {
Entries entries;
// {...} uncompress data and parse content.

View File

@ -0,0 +1,33 @@
---
title: all
---
# QtPromise::all
*Since: 0.5.0*
```
QtPromise::all(Sequence<QPromise<T>> promises) -> QPromise<QVector<T>>
QtPromise::all(Sequence<QPromise<void>> promises) -> QPromise<void>
```
Returns a `QPromise<QVector<T>>` (or `QPromise<void>`) that fulfills when **all** `promises` of (the same) type `T` have been fulfilled. The `output` value is a vector containing all the values of `promises`, in the same order, i.e., at the respective positions to the original sequence, regardless of completion order.
If any of the given `promises` fail, `output` immediately rejects with the error of the promise that rejected, whether or not the other promises are resolved.
`Sequence` is any STL compatible container (eg. `QVector`, `QList`, `std::vector`, etc.)
```cpp
QVector<QPromise<QByteArray> > promises{
download(QUrl("http://a...")),
download(QUrl("http://b...")),
download(QUrl("http://c..."))
};
auto output = QtPromise::all(promises);
// output type: QPromise<QVector<QByteArray>>
output.then([](const QVector<QByteArray>& res) {
// {...}
});
```

View File

@ -0,0 +1,42 @@
---
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,48 @@
---
title: connect
---
# QtPromise::connect
*Since: 0.5.0*
```cpp
(1) QtPromise::connect(QObject* sender, Signal(T) resolver) -> QPromise<T>
(2) QtPromise::connect(QObject* sender, Signal(T) resolver, Signal(R) rejecter) -> QPromise<T>
(3) QtPromise::connect(QObject* sender, Signal(T) resolver, QObject* sender2, Signal(R) rejecter) -> QPromise<T>
```
Creates a `QPromise<T>` that will be fulfilled with the `resolver` signal's first argument, or a `QPromise<void>` if `resolver` doesn't provide any argument.
The second `(2)` and third `(3)` variants of this method will reject the `output` promise when the `rejecter` signal is emitted. The rejection reason is the value of the `rejecter` signal's first argument or [`QPromiseUndefinedException`](../exceptions/undefined) if `rejected` doesn't provide any argument.
Additionally, the `output` promise will be automatically rejected with [`QPromiseContextException`](../exceptions/context.md) if `sender` is destroyed before the promise is resolved (that doesn't apply to `sender2`).
```cpp
class Sender : public QObject
{
Q_SIGNALS:
void finished(const QByteArray&);
void error(ErrorCode);
};
auto sender = new Sender();
auto output = QtPromise::connect(sender, &Sender::finished, &Sender::error);
// 'output' resolves as soon as one of the following events happens:
// - the 'sender' object is destroyed, the promise is rejected
// - the 'finished' signal is emitted, the promise is fulfilled
// - the 'error' signal is emitted, the promise is rejected
// 'output' type: QPromise<QByteArray>
output.then([](const QByteArray& res) {
// 'res' is the first argument of the 'finished' signal.
}).fail([](ErrorCode err) {
// 'err' is the first argument of the 'error' signal.
}).fail([](const QPromiseContextException& err) {
// the 'sender' object has been destroyed before any of
// the 'finished' or 'error' signals have been emitted.
});
```
See also the [`Qt Signals`](../qtsignals.md) section for more examples.

View File

@ -0,0 +1,41 @@
---
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,46 @@
---
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,46 @@
---
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,15 +0,0 @@
## `qPromise`
```
qPromise(T value) -> QPromise<R>
```
Similar to the [`QPromise<T>::resolve`](../qpromise/resolve.md) static method, creates a promise resolved from a given `value` without the extra typing:
```cpp
auto promise = qPromise(); // QPromise<void>
auto promise = qPromise(42); // QPromise<int>
auto promise = qPromise(QString("foo")); // QPromise<QString>
```
This method also allows to convert `QFuture<T>` to `QPromise<T>` delayed until the `QFuture` is finished ([read more](../qtconcurrent.md#convert)).

View File

@ -1,16 +0,0 @@
## `qPromiseAll`
```
qPromiseAll(Sequence<QPromise<T>> promises) -> QPromise<QVector<T>>
qPromiseAll(Sequence<QPromise<void>> promises) -> QPromise<void>
```
This method simply calls the appropriated [`QPromise<T>::all`](../qpromise/all.md) static method based on the given `QVector` type. In some cases, this method is more convenient than the static one since it avoid some extra typing:
```cpp
QVector<QPromise<QByteArray> > promises{...}
auto output = qPromiseAll(promises);
// eq. QPromise<QByteArray>::all(promises)
```

View File

@ -0,0 +1,48 @@
---
title: reduce
---
# QtPromise::reduce
*Since: 0.5.0*
```cpp
QPromise::reduce(Sequence<T|QPromise<T>> values, Reducer reducer) -> QPromise<T>
QPromise::reduce(Sequence<T|QPromise<T>> values, Reducer reducer, R|QPromise<R> initialValue) -> QPromise<R>
// With:
// - Sequence: STL compatible container (e.g. QVector, etc.)
// - Reducer: Function(T|R accumulator, T item, int index) -> R|QPromise<R>
```
Iterates over `values` and [reduces the sequence to a single value](https://en.wikipedia.org/wiki/Fold_%28higher-order_function%29) using the given `reducer` function and an optional `initialValue`. The type returned by `reducer` determines the type of the `output` promise. If `reducer` throws, `output` is rejected with the new exception.
If `reducer` returns a promise (or `QFuture`), then the result of the promise is awaited, before continuing with next iteration. If any promise in the `values` sequence is rejected or a promise returned by the `reducer` function is rejected, `output` immediately rejects with the error of the promise that rejected.
```cpp
// Concatenate the content of the given files, read asynchronously
auto output = QtPromise::reduce(QList<QUrl>{
"file:f0.txt", // contains "foo"
"file:f1.txt", // contains "bar"
"file:f2.txt" // contains "42"
}, [](const QString& acc, const QString& cur, int idx) {
return readAsync(cur).then([=](const QString& res) {
return QString("%1;%2:%3").arg(acc).arg(idx).arg(res);
});
}, QString("index:text"));
// 'output' resolves as soon as all promises returned by
// 'reducer' are fulfilled or at least one is rejected.
// 'output' type: QPromise<QString>
output.then([](const QString& res) {
// res == "index:text;0:foo;1:bar;2:42"
// {...}
});
```
::: warning IMPORTANT
The first time `reducer` is called, if no `initialValue` is provided, `accumulator` will be equal to the first value in the sequence, and `currentValue` to the second one (thus index will be `1`).
:::
See also: [`QPromise<T>::reduce`](../qpromise/reduce.md)

View File

@ -0,0 +1,21 @@
---
title: resolve
---
# QtPromise::resolve
*Since: 0.5.0*
```
QtPromise::resolve(T value) -> QPromise<R>
```
Similar to the [`QPromise<T>::resolve`](../qpromise/resolve.md) static method, creates a promise resolved from a given `value` but without the extra typing:
```cpp
auto promise = QtPromise::resolve(); // QPromise<void>
auto promise = QtPromise::resolve(42); // QPromise<int>
auto promise = QtPromise::resolve(QString("foo")); // QPromise<QString>
```
This method also allows to convert `QFuture<T>` to `QPromise<T>`, delayed until the `QFuture` is finished ([read more](../qtconcurrent.md#convert)).

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>>
@ -25,4 +31,4 @@ output.then([](const QVector<QByteArray>& res) {
});
```
See also: [`qPromiseAll`](../helpers/qpromiseall.md)
See also: [`QtPromise::all`](../helpers/all.md)

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**
@ -27,3 +35,33 @@ QPromise<int> promise([](const auto& resolve, const auto& reject) {
// {...}
});
```
**Undefined rejection reason**
*Since: 0.5.0*
While not recommended because it makes tracking errors more difficult, it's also possible to reject a promise without explicit reason, in which case, a built-in [`QPromiseUndefinedException`](../exceptions/undefined.md) is thrown:
```cpp
QPromise<int> promise([](const QPromiseResolve<int>& resolve, const QPromiseReject<int>& reject) {
async_method([=](bool success, int result) {
if (success) {
resolve(result);
} else {
reject();
}
});
});
```
```cpp
// The exception can be caught explicitly
promise.fail([](const QPromiseUndefinedException&) {
// { ... }
})
// ... or implicitly (since undefined)
promise.fail([]() {
// { ... }
})
```

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,49 @@
---
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,62 @@
---
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

@ -0,0 +1,58 @@
---
title: .reduce
---
# QPromise::reduce
*Since: 0.5.0*
```cpp
QPromise<Sequence<T>>::reduce(Reducer reducer) -> QPromise<T>
QPromise<Sequence<T>>::reduce(Reducer reducer, R|QPromise<R> initialValue) -> QPromise<R>
// With:
// - Sequence: STL compatible container (e.g. QVector, etc.)
// - Reducer: Function(T|R accumulator, T item, 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 [reduces the sequence to a single value](https://en.wikipedia.org/wiki/Fold_%28higher-order_function%29) using the given `reducer` function and an optional `initialValue`. The type returned by `reducer` determines the type of the `output` promise.
See [`QtPromise::reduce`](../helpers/reduce.md) for details, this method is provided for convenience and is similar to:
```cpp
promise.then([](const T& values) {
return QtPromise::reduce(values, reducer, initialValue);
})
```
For example:
```cpp
auto input = QtPromise::resolve(QList<QUrl>{
"file:f0.txt", // contains "foo"
"file:f1.txt", // contains "bar"
"file:f2.txt" // contains "42"
});
// Concatenate the content of the given files, read asynchronously
auto output = input.reduce([](const QString& acc, const QString& cur, int idx) {
return readAsync(cur).then([=](const QString& res) {
return QString("%1;%2:%3").arg(acc).arg(idx).arg(res);
});
}, QString("index:text"));
// 'output' resolves as soon as all promises returned by
// 'reducer' are fulfilled or at least one is rejected.
// 'output' type: QPromise<QString>
output.then([](const QString& res) {
// res == "index:text;0:foo;1:bar;2:42"
// {...}
});
```
See also: [`QtPromise::reduce`](../helpers/reduce.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>
@ -19,4 +25,4 @@ QPromise<int> compute(const QString& type)
}
```
See also: [`qPromise`](../helpers/qpromise.md)
See also: [`QtPromise::resolve`](../helpers/resolve.md)

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,9 @@ 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 +41,9 @@ 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,10 +1,16 @@
## `QPromise<T>::timeout`
---
title: .timeout
---
```
# QPromise::timeout
*Since: 0.2.0*
```cpp
QPromise<T>::timeout(int msec, any error = QPromiseTimeoutException) -> QPromise<T>
```
This method returns a promise that will be resolved with the `input` promise's fulfillment value or rejection reason. However, if the `input` promise is not fulfilled or rejected within `msec` milliseconds, the `output` promise is rejected with `error` as the reason (`QPromiseTimeoutException` by default).
This method returns a promise that will be resolved with the `input` promise's fulfillment value or rejection reason. However, if the `input` promise is not fulfilled or rejected within `msec` milliseconds, the `output` promise is rejected with `error` as the reason ([`QPromiseTimeoutException`](../exceptions/timeout.md) by default).
```cpp
QPromise<int> input = {...}

View File

@ -1,6 +1,12 @@
## `QPromise<T>::wait`
---
title: .wait
---
```
# QPromise::wait
*Since: 0.1.0*
```cpp
QPromise<T>::wait() -> QPromise<T>
```
@ -9,7 +15,7 @@ This method holds the execution of the remaining code until the `input` promise
```cpp
int result = -1;
QPromise<int> input = qPromise(QtConcurrent::run([]() {
QPromise<int> input = QtPromise::resolve(QtConcurrent::run([]() {
return 42;
})).tap([&](int res) {
result = res;

View File

@ -1,10 +1,10 @@
## QtConcurrent
# Qt Concurrent
QtPromise integrates with [QtConcurrent](https://doc.qt.io/qt-5/qtconcurrent-index.html) to make easy chaining QFuture with QPromise.
## <a name="qtconcurrent-convert"></a> Convert
Converting `QFuture<T>` to `QPromise<T>` is done using the [`qPromise`](helpers/qpromise.md) helper:
Converting `QFuture<T>` to `QPromise<T>` is done using the [`QtPromise::resolve`](helpers/resolve.md) helper:
```cpp
QFuture<int> future = QtConcurrent::run([]() {
@ -12,13 +12,13 @@ QFuture<int> future = QtConcurrent::run([]() {
return 42;
});
QPromise<int> promise = qPromise(future);
QPromise<int> promise = QtPromise::resolve(future);
```
or simply:
```cpp
auto promise = qPromise(QtConcurrent::run([]() {
auto promise = QtPromise::resolve(QtConcurrent::run([]() {
// {...}
}));
```
@ -46,7 +46,7 @@ The `output` promise is resolved when the `QFuture` is [finished](https://doc.qt
## Error
Exceptions thrown from a QtConcurrent thread reject the associated promise with the exception as the reason. Note that if you throw an exception that is not a subclass of `QException`, the promise with be rejected with [`QUnhandledException`](https://doc.qt.io/qt-5/qunhandledexception.html#details) (this restriction only applies to exceptions thrown from a QtConcurrent thread, [read more](https://doc.qt.io/qt-5/qexception.html#details)).
Exceptions thrown from a QtConcurrent thread reject the associated promise with the exception as the reason. Note that if you throw an exception that is not a subclass of `QException`, the promise will be rejected with [`QUnhandledException`](https://doc.qt.io/qt-5/qunhandledexception.html#details) (this restriction only applies to exceptions thrown from a QtConcurrent thread, [read more](https://doc.qt.io/qt-5/qexception.html#details)).
```cpp
QPromise<int> promise = ...

View File

@ -0,0 +1,89 @@
# Qt Signals
QtPromise supports creating promises that are resolved or rejected by regular [Qt signals](https://doc.qt.io/qt-5/signalsandslots.html).
::: warning IMPORTANT
A promise connected to a signal will be resolved (fulfilled or rejected) **only one time**, no matter if the signals are emitted multiple times. Internally, the promise is disconnected from all signals as soon as one signal is emitted.
:::
## Resolve Signal
The [`QtPromise::connect()`](helpers/connect.md) helper allows to create a promise resolved from a single signal:
```cpp
// [signal] Object::finished(const QByteArray&)
auto output = QtPromise::connect(obj, &Object::finished);
// output type: QPromise<QByteArray>
output.then([](const QByteArray& data) {
// {...}
});
```
If the signal doesn't provide any argument, a `QPromise<void>` is returned:
```cpp
// [signal] Object::done()
auto output = QtPromise::connect(obj, &Object::done);
// output type: QPromise<void>
output.then([]() {
// {...}
});
```
::: tip NOTE
QtPromise currently only supports single argument signals, which means that only the first argument is used to fulfill or reject the connected promise, other arguments being ignored.
:::
## Reject Signal
The [`QtPromise::connect()`](helpers/connect.md) helper also allows to reject the promise from another signal:
```cpp
// [signal] Object::finished(const QByteArray& data)
// [signal] Object::error(ObjectError error)
auto output = QtPromise::connect(obj, &Object::finished, &Object::error);
// output type: QPromise<QByteArray>
output.then([](const QByteArray& data) {
// {...}
}).fail(const ObjectError& error) {
// {...}
});
```
If the rejection signal doesn't provide any argument, the promise will be rejected
with [`QPromiseUndefinedException`](../exceptions/undefined), for example:
```cpp
// [signal] Object::finished()
// [signal] Object::error()
auto output = QtPromise::connect(obj, &Object::finished, &Object::error);
// output type: QPromise<QByteArray>
output.then([]() {
// {...}
}).fail(const QPromiseUndefinedException& error) {
// {...}
});
```
A third variant allows to connect the resolve and reject signals from different objects:
```cpp
// [signal] ObjectA::finished(const QByteArray& data)
// [signal] ObjectB::error(ObjectBError error)
auto output = QtPromise::connect(objA, &ObjectA::finished, objB, &ObjectB::error);
// output type: QPromise<QByteArray>
output.then([](const QByteArray& data) {
// {...}
}).fail(const ObjectBError& error) {
// {...}
});
```
Additionally to the rejection signal, promises created using [`QtPromise::connect()`](helpers/connect.md) are automatically rejected with [`QPromiseContextException`](exceptions/context.md) if the sender is destroyed before fulfilling the promise.
See [`QtPromise::connect()`](helpers/connect.md) for more details.

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

@ -2,7 +2,8 @@
#define QTPROMISE_MODULE_H
#include "../src/qtpromise/qpromise.h"
#include "../src/qtpromise/qpromiseconnections.h"
#include "../src/qtpromise/qpromisefuture.h"
#include "../src/qtpromise/qpromisehelpers.h"
#endif // ifndef QTPROMISE_MODULE_H
#endif // QTPROMISE_MODULE_H

View File

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

View File

@ -1,10 +1,10 @@
#ifndef QTPROMISE_QPROMISE_H
#define QTPROMISE_QPROMISE_H
// QtPromise
#include "qpromise_p.h"
#include "qpromiseerror.h"
#include "qpromiseexceptions.h"
#include "qpromiseglobal.h"
#include "qpromiseresolver.h"
// Qt
#include <QExplicitlySharedDataPointer>
@ -76,6 +76,7 @@ public: // STATIC
protected:
friend struct QtPromisePrivate::PromiseFulfill<QPromise<T>>;
friend class QtPromisePrivate::PromiseResolver<T>;
friend struct QtPromisePrivate::PromiseInspect;
QExplicitlySharedDataPointer<QtPromisePrivate::PromiseData<T>> m_d;
};
@ -87,9 +88,32 @@ 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);
template <typename Functor, typename Input>
inline typename QtPromisePrivate::PromiseDeduce<Input>::Type
reduce(Functor fn, Input initial);
template <typename Functor, typename U = T>
inline typename QtPromisePrivate::PromiseDeduce<typename U::value_type>::Type
reduce(Functor fn);
public: // STATIC
// DEPRECATED (remove at version 1)
template <template <typename, typename...> class Sequence = QVector, typename ...Args>
inline static QPromise<QVector<T>> all(const Sequence<QPromise<T>, Args...>& promises);
Q_DECL_DEPRECATED_X("Use QtPromise::all instead") static inline QPromise<QVector<T>>
all(const Sequence<QPromise<T>, Args...>& promises);
inline static QPromise<T> resolve(const T& value);
inline static QPromise<T> resolve(T&& value);
@ -106,8 +130,11 @@ public:
QPromise(F&& resolver): QPromiseBase<void>(std::forward<F>(resolver)) { }
public: // STATIC
// DEPRECATED (remove at version 1)
template <template <typename, typename...> class Sequence = QVector, typename ...Args>
inline static QPromise<void> all(const Sequence<QPromise<void>, Args...>& promises);
Q_DECL_DEPRECATED_X("Use QtPromise::all instead") static inline QPromise<void>
all(const Sequence<QPromise<void>, Args...>& promises);
inline static QPromise<void> resolve();
@ -119,4 +146,4 @@ private:
#include "qpromise.inl"
#endif // ifndef QTPROMISE_QPROMISE_H
#endif // QTPROMISE_QPROMISE_H

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)
@ -194,40 +156,74 @@ inline QPromise<T> QPromiseBase<T>::reject(E&& error)
}
template <typename T>
template <template <typename, typename...> class Sequence, typename ...Args>
inline QPromise<QVector<T>> QPromise<T>::all(const Sequence<QPromise<T>, Args...>& promises)
template <typename Functor>
inline QPromise<T> QPromise<T>::each(Functor fn)
{
const int count = static_cast<int>(promises.size());
if (count == 0) {
return QPromise<QVector<T>>::resolve({});
}
return QPromise<QVector<T>>([=](
const QPromiseResolve<QVector<T>>& resolve,
const QPromiseReject<QVector<T>>& reject) {
QSharedPointer<int> remaining(new int(count));
QSharedPointer<QVector<T>> results(new QVector<T>(count));
return this->tap([=](const T& values) {
int i = 0;
for (const auto& promise: promises) {
promise.then([=](const T& res) mutable {
(*results)[i] = res;
if (--(*remaining) == 0) {
resolve(*results);
}
}, [=]() mutable {
if (*remaining != -1) {
*remaining = -1;
reject(std::current_exception());
}
});
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 QtPromise::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 <typename Functor, typename Input>
inline typename QtPromisePrivate::PromiseDeduce<Input>::Type
QPromise<T>::reduce(Functor fn, Input initial)
{
return this->then([=](const T& values) {
return QtPromise::reduce(values, fn, initial);
});
}
template <typename T>
template <typename Functor, typename U>
inline typename QtPromisePrivate::PromiseDeduce<typename U::value_type>::Type
QPromise<T>::reduce(Functor fn)
{
return this->then([=](const T& values) {
return QtPromise::reduce(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)
{
return QtPromise::all(promises);
}
template <typename T>
inline QPromise<T> QPromise<T>::resolve(const T& value)
{
@ -247,37 +243,12 @@ inline QPromise<T> QPromise<T>::resolve(T&& value)
template <template <typename, typename...> class Sequence, typename ...Args>
inline QPromise<void> QPromise<void>::all(const Sequence<QPromise<void>, Args...>& promises)
{
const int count = static_cast<int>(promises.size());
if (count == 0) {
return QPromise<void>::resolve();
}
return QPromise<void>([=](
const QPromiseResolve<void>& resolve,
const QPromiseReject<void>& reject) {
QSharedPointer<int> remaining(new int(count));
for (const auto& promise: promises) {
promise.then([=]() {
if (--(*remaining) == 0) {
resolve();
}
}, [=]() {
if (*remaining != -1) {
*remaining = -1;
reject(std::current_exception());
}
});
}
});
return QtPromise::all(promises);
}
inline QPromise<void> QPromise<void>::resolve()
{
return QPromise<void>([](const QPromiseResolve<void>& resolve) {
resolve();
});
return QtPromise::resolve();
}
} // namespace QtPromise

View File

@ -1,7 +1,6 @@
#ifndef QTPROMISE_QPROMISE_P_H
#define QTPROMISE_QPROMISE_P_H
// QtPromise
#include "qpromiseglobal.h"
// Qt
@ -111,33 +110,54 @@ private:
template <typename T>
struct PromiseDeduce
{
using Type = QtPromise::QPromise<Unqualified<T>>;
using Type = QtPromise::QPromise<T>;
};
template <typename T>
struct PromiseDeduce<T&>
: public PromiseDeduce<T>
{ };
template <typename T>
struct PromiseDeduce<const T>
: public PromiseDeduce<T>
{ };
template <typename T>
struct PromiseDeduce<const volatile T>
: public PromiseDeduce<T>
{ };
template <typename T>
struct PromiseDeduce<QtPromise::QPromise<T>>
: public PromiseDeduce<T>
{ };
template <typename Functor, typename... Args>
struct PromiseFunctor
{
using ResultType = typename std::result_of<Functor(Args...)>::type;
using PromiseType = typename PromiseDeduce<ResultType>::Type;
};
template <typename T>
struct PromiseFulfill
{
static void call(
T&& value,
const QtPromise::QPromiseResolve<T>& resolve,
const QtPromise::QPromiseReject<T>&)
template <typename V, typename TResolve, typename TReject>
static void call(V&& value, const TResolve& resolve, const TReject&)
{
resolve(std::move(value));
resolve(std::forward<V>(value));
}
};
template <typename T>
struct PromiseFulfill<QtPromise::QPromise<T>>
{
template <typename TResolve, typename TReject>
static void call(
const QtPromise::QPromise<T>& promise,
const QtPromise::QPromiseResolve<T>& resolve,
const QtPromise::QPromiseReject<T>& reject)
const TResolve& resolve,
const TReject& reject)
{
if (promise.isFulfilled()) {
resolve(promise.m_d->value());
@ -176,51 +196,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());
}
@ -228,15 +214,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());
@ -248,7 +232,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(
@ -257,7 +241,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);
};
}
};
@ -266,7 +250,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(
@ -275,7 +259,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);
};
}
};
@ -284,7 +268,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(
@ -293,7 +277,7 @@ struct PromiseHandler<void, THandler, void>
const TReject& reject)
{
return [=]() {
PromiseDispatch<void, ResType>::call(handler, resolve, reject);
PromiseDispatch<ResType>::call(resolve, reject, handler);
};
}
};
@ -351,7 +335,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());
}
@ -374,7 +358,7 @@ struct PromiseCatcher<T, THandler, void>
try {
error.rethrow();
} catch (...) {
PromiseDispatch<void, ResType>::call(handler, resolve, reject);
PromiseDispatch<ResType>::call(resolve, reject, handler);
}
};
}
@ -397,6 +381,18 @@ struct PromiseCatcher<T, std::nullptr_t, void>
}
};
template <typename T, typename F>
struct PromiseMapper
{ };
template <typename T, typename F, template <typename, typename...> class Sequence, typename ...Args>
struct PromiseMapper<Sequence<T, Args...>, F>
{
using ReturnType = typename std::result_of<F(T, int)>::type;
using ResultType = QVector<typename PromiseDeduce<ReturnType>::Type::Type>;
using PromiseType = QtPromise::QPromise<ResultType>;
};
template <typename T> class PromiseData;
template <typename T, typename F>
@ -562,68 +558,15 @@ protected:
}
};
template <typename T>
class PromiseResolver
struct PromiseInspect
{
public:
PromiseResolver(QtPromise::QPromise<T> promise)
: m_d(new Data())
template <typename T>
static inline PromiseData<T>* get(const QtPromise::QPromise<T>& p)
{
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;
return p.m_d.data();
}
};
} // namespace QtPromise
} // namespace QtPromisePrivate
#endif // ifndef QTPROMISE_QPROMISE_H
#endif // QTPROMISE_QPROMISE_H

View File

@ -0,0 +1,48 @@
#ifndef QTPROMISE_QPROMISECONNECTIONS_H
#define QTPROMISE_QPROMISECONNECTIONS_H
// Qt
#include <QSharedPointer>
namespace QtPromise {
class QPromiseConnections
{
public:
QPromiseConnections() : m_d(new Data()) { }
int count() const { return m_d->connections.count(); }
void disconnect() const { m_d->disconnect(); }
void operator<<(QMetaObject::Connection&& other) const
{
m_d->connections.append(std::move(other));
}
private:
struct Data
{
QVector<QMetaObject::Connection> connections;
~Data() {
if (!connections.empty()) {
qWarning("QPromiseConnections: destroyed with unhandled connections.");
disconnect();
}
}
void disconnect() {
for (const auto& connection: connections) {
QObject::disconnect(connection);
}
connections.clear();
}
};
QSharedPointer<Data> m_d;
};
} // namespace QtPromise
#endif // QTPROMISE_QPROMISECONNECTIONS_H

View File

@ -1,31 +0,0 @@
#ifndef QTPROMISE_QPROMISEERROR_H
#define QTPROMISE_QPROMISEERROR_H
// QtPromise
#include "qpromise_p.h"
#include "qpromiseglobal.h"
// Qt
#include <QException>
namespace QtPromise {
class QPromiseTimeoutException : public QException
{
public:
void raise() const Q_DECL_OVERRIDE { throw *this; }
QPromiseTimeoutException* clone() const Q_DECL_OVERRIDE
{
return new QPromiseTimeoutException(*this);
}
};
// QPromiseError is provided for backward compatibility and will be
// removed in the next major version: it wasn't intended to be used
// directly and thus should not be part of the public API.
// TODO Remove QPromiseError at version 1.0
using QPromiseError = QtPromisePrivate::PromiseError;
} // namespace QtPromise
#endif // QTPROMISE_QPROMISEERROR_H

View File

@ -0,0 +1,60 @@
#ifndef QTPROMISE_QPROMISEEXCEPTIONS_H
#define QTPROMISE_QPROMISEEXCEPTIONS_H
#include "qpromise_p.h"
#include "qpromiseglobal.h"
// Qt
#include <QException>
namespace QtPromise {
class QPromiseCanceledException : public QException
{
public:
void raise() const Q_DECL_OVERRIDE { throw *this; }
QPromiseCanceledException* clone() const Q_DECL_OVERRIDE
{
return new QPromiseCanceledException(*this);
}
};
class QPromiseContextException : public QException
{
public:
void raise() const Q_DECL_OVERRIDE { throw *this; }
QPromiseContextException* clone() const Q_DECL_OVERRIDE
{
return new QPromiseContextException(*this);
}
};
class QPromiseTimeoutException : public QException
{
public:
void raise() const Q_DECL_OVERRIDE { throw *this; }
QPromiseTimeoutException* clone() const Q_DECL_OVERRIDE
{
return new QPromiseTimeoutException(*this);
}
};
class QPromiseUndefinedException : public QException
{
public:
void raise() const Q_DECL_OVERRIDE { throw *this; }
QPromiseUndefinedException* clone() const Q_DECL_OVERRIDE
{
return new QPromiseUndefinedException(*this);
}
};
// QPromiseError is provided for backward compatibility and will be
// removed in the next major version: it wasn't intended to be used
// directly and thus should not be part of the public API.
// TODO Remove QPromiseError at version 1.0
using QPromiseError = QtPromisePrivate::PromiseError;
} // namespace QtPromise
#endif // QTPROMISE_QPROMISEEXCEPTIONS_H

View File

@ -1,24 +1,12 @@
#ifndef QTPROMISE_QPROMISEFUTURE_P_H
#define QTPROMISE_QPROMISEFUTURE_P_H
#include "qpromiseexceptions.h"
// Qt
#include <QFutureWatcher>
#include <QFuture>
namespace QtPromise {
class QPromiseCanceledException : public QException
{
public:
void raise() const Q_DECL_OVERRIDE { throw *this; }
QPromiseCanceledException* clone() const Q_DECL_OVERRIDE
{
return new QPromiseCanceledException(*this);
}
};
} // namespace QtPromise
namespace QtPromisePrivate {
template <typename T>

View File

@ -122,4 +122,4 @@ struct ArgsOf<const volatile T&&> : public ArgsOf<T>
} // namespace QtPromisePrivate
#endif // ifndef QTPROMISE_QPROMISEGLOBAL_H
#endif // QTPROMISE_QPROMISEGLOBAL_H

View File

@ -1,41 +1,297 @@
#ifndef QTPROMISE_QPROMISEHELPERS_H
#define QTPROMISE_QPROMISEHELPERS_H
// QtPromise
#include "qpromise_p.h"
#include "qpromisehelpers_p.h"
namespace QtPromise {
template <typename T>
static inline typename QtPromisePrivate::PromiseDeduce<T>::Type qPromise(T&& value)
static inline typename QtPromisePrivate::PromiseDeduce<T>::Type
resolve(T&& value)
{
using namespace QtPromisePrivate;
using Promise = typename PromiseDeduce<T>::Type;
return Promise([&](
const QPromiseResolve<typename Promise::Type>& resolve,
const QPromiseReject<typename Promise::Type>& reject) {
PromiseFulfill<T>::call(std::forward<T>(value), resolve, reject);
using PromiseType = typename PromiseDeduce<T>::Type;
using ValueType = typename PromiseType::Type;
using ResolveType = QPromiseResolve<ValueType>;
using RejectType = QPromiseReject<ValueType>;
return PromiseType([&](ResolveType&& resolve, RejectType&& reject) {
PromiseFulfill<Unqualified<T>>::call(
std::forward<T>(value),
std::forward<ResolveType>(resolve),
std::forward<RejectType>(reject));
});
}
static inline QPromise<void> qPromise()
template <typename T>
static inline QPromise<T>
resolve(QPromise<T> value)
{
return QPromise<void>([](
const QPromiseResolve<void>& resolve) {
return std::move(value);
}
static inline QPromise<void>
resolve()
{
return QPromise<void>([](const QPromiseResolve<void>& resolve) {
resolve();
});
}
template <typename T, template <typename, typename...> class Sequence = QVector, typename ...Args>
static inline QPromise<QVector<T>> qPromiseAll(const Sequence<QPromise<T>, Args...>& promises)
static inline QPromise<QVector<T>>
all(const Sequence<QPromise<T>, Args...>& promises)
{
return QPromise<T>::all(promises);
const int count = static_cast<int>(promises.size());
if (count == 0) {
return QtPromise::resolve(QVector<T>{});
}
return QPromise<QVector<T>>([=](
const QPromiseResolve<QVector<T>>& resolve,
const QPromiseReject<QVector<T>>& reject) {
QSharedPointer<int> remaining(new int(count));
QSharedPointer<QVector<T>> results(new QVector<T>(count));
int i = 0;
for (const auto& promise: promises) {
promise.then([=](const T& res) mutable {
(*results)[i] = res;
if (--(*remaining) == 0) {
resolve(*results);
}
}, [=]() mutable {
if (*remaining != -1) {
*remaining = -1;
reject(std::current_exception());
}
});
i++;
}
});
}
template <template <typename, typename...> class Sequence = QVector, typename ...Args>
static inline QPromise<void> qPromiseAll(const Sequence<QPromise<void>, Args...>& promises)
static inline QPromise<void>
all(const Sequence<QPromise<void>, Args...>& promises)
{
return QPromise<void>::all(promises);
const int count = static_cast<int>(promises.size());
if (count == 0) {
return QtPromise::resolve();
}
return QPromise<void>([=](
const QPromiseResolve<void>& resolve,
const QPromiseReject<void>& reject) {
QSharedPointer<int> remaining(new int(count));
for (const auto& promise: promises) {
promise.then([=]() {
if (--(*remaining) == 0) {
resolve();
}
}, [=]() {
if (*remaining != -1) {
*remaining = -1;
reject(std::current_exception());
}
});
}
});
}
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 Sender, typename Signal>
static inline typename QtPromisePrivate::PromiseFromSignal<Signal>
connect(const Sender* sender, Signal signal)
{
using namespace QtPromisePrivate;
using T = typename PromiseFromSignal<Signal>::Type;
return QPromise<T>(
[&](const QPromiseResolve<T>& resolve, const QPromiseReject<T>& reject) {
QPromiseConnections connections;
connectSignalToResolver(connections, resolve, sender, signal);
connectDestroyedToReject(connections, reject, sender);
});
}
template <typename FSender, typename FSignal, typename RSender, typename RSignal>
static inline typename QtPromisePrivate::PromiseFromSignal<FSignal>
connect(const FSender* fsender, FSignal fsignal, const RSender* rsender, RSignal rsignal)
{
using namespace QtPromisePrivate;
using T = typename PromiseFromSignal<FSignal>::Type;
return QPromise<T>(
[&](const QPromiseResolve<T>& resolve, const QPromiseReject<T>& reject) {
QPromiseConnections connections;
connectSignalToResolver(connections, resolve, fsender, fsignal);
connectSignalToResolver(connections, reject, rsender, rsignal);
connectDestroyedToReject(connections, reject, fsender);
});
}
template <typename Sender, typename FSignal, typename RSignal>
static inline typename QtPromisePrivate::PromiseFromSignal<FSignal>
connect(const Sender* sender, FSignal fsignal, RSignal rsignal)
{
return connect(sender, fsignal, sender, rsignal);
}
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 QtPromise::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;
});
}
template <typename T, template <typename...> class Sequence = QVector, typename Reducer, typename Input, typename ...Args>
static inline typename QtPromisePrivate::PromiseDeduce<Input>::Type
reduce(const Sequence<T, Args...>& values, Reducer fn, Input initial)
{
using namespace QtPromisePrivate;
using PromiseType = typename PromiseDeduce<T>::Type;
using ValueType = typename PromiseType::Type;
int idx = 0;
auto promise = QtPromise::resolve(std::move(initial));
for (const auto& value : values) {
auto input = QtPromise::resolve(value);
promise = promise.then([=]() {
return input.then([=](const ValueType& cur) {
return fn(PromiseInspect::get(promise)->value().data(), cur, idx);
});
});
idx++;
}
return promise;
}
template <typename T, template <typename...> class Sequence = QVector, typename Reducer, typename ...Args>
static inline typename QtPromisePrivate::PromiseDeduce<T>::Type
reduce(const Sequence<T, Args...>& values, Reducer fn)
{
using namespace QtPromisePrivate;
using PromiseType = typename PromiseDeduce<T>::Type;
using ValueType = typename PromiseType::Type;
Q_ASSERT(values.size());
int idx = 1;
auto it = values.begin();
auto promise = QtPromise::resolve(*it);
for (++it; it != values.end(); ++it) {
auto input = QtPromise::resolve(*it);
promise = promise.then([=]() {
return input.then([=](const ValueType& cur) {
return fn(PromiseInspect::get(promise)->value().data(), cur, idx);
});
});
idx++;
}
return promise;
}
// DEPRECATIONS (remove at version 1)
template <typename... Args>
Q_DECL_DEPRECATED_X("Use QtPromise::resolve instead")
static inline auto
qPromise(Args&&... args)
-> decltype(QtPromise::resolve(std::forward<Args>(args)...))
{
return QtPromise::resolve(std::forward<Args>(args)...);
}
template <typename... Args>
Q_DECL_DEPRECATED_X("Use QtPromise::all instead")
static inline auto
qPromiseAll(Args&&... args)
-> decltype(QtPromise::all(std::forward<Args>(args)...))
{
return QtPromise::all(std::forward<Args>(args)...);
}
} // namespace QtPromise

View File

@ -0,0 +1,91 @@
#ifndef QTPROMISE_QPROMISEHELPERS_P_H
#define QTPROMISE_QPROMISEHELPERS_P_H
#include "qpromiseconnections.h"
#include "qpromiseexceptions.h"
namespace QtPromisePrivate {
// TODO: Suppress QPrivateSignal trailing private signal args
// TODO: Support deducing tuple from args (might require MSVC2017)
template <typename Signal>
using PromiseFromSignal = typename QtPromise::QPromise<Unqualified<typename ArgsOf<Signal>::first>>;
// Connect signal() to QPromiseResolve
template <typename Sender, typename Signal>
typename std::enable_if<(ArgsOf<Signal>::count == 0)>::type
connectSignalToResolver(
const QtPromise::QPromiseConnections& connections,
const QtPromise::QPromiseResolve<void>& resolve,
const Sender* sender,
Signal signal)
{
connections << QObject::connect(sender, signal, [=]() {
connections.disconnect();
resolve();
});
}
// Connect signal() to QPromiseReject
template <typename T, typename Sender, typename Signal>
typename std::enable_if<(ArgsOf<Signal>::count == 0)>::type
connectSignalToResolver(
const QtPromise::QPromiseConnections& connections,
const QtPromise::QPromiseReject<T>& reject,
const Sender* sender,
Signal signal)
{
connections << QObject::connect(sender, signal, [=]() {
connections.disconnect();
reject(QtPromise::QPromiseUndefinedException());
});
}
// Connect signal(args...) to QPromiseResolve
template <typename T, typename Sender, typename Signal>
typename std::enable_if<(ArgsOf<Signal>::count >= 1)>::type
connectSignalToResolver(
const QtPromise::QPromiseConnections& connections,
const QtPromise::QPromiseResolve<T>& resolve,
const Sender* sender,
Signal signal)
{
connections << QObject::connect(sender, signal, [=](const T& value) {
connections.disconnect();
resolve(value);
});
}
// Connect signal(args...) to QPromiseReject
template <typename T, typename Sender, typename Signal>
typename std::enable_if<(ArgsOf<Signal>::count >= 1)>::type
connectSignalToResolver(
const QtPromise::QPromiseConnections& connections,
const QtPromise::QPromiseReject<T>& reject,
const Sender* sender,
Signal signal)
{
using V = Unqualified<typename ArgsOf<Signal>::first>;
connections << QObject::connect(sender, signal, [=](const V& value) {
connections.disconnect();
reject(value);
});
}
// Connect QObject::destroyed signal to QPromiseReject
template <typename T, typename Sender>
void connectDestroyedToReject(
const QtPromise::QPromiseConnections& connections,
const QtPromise::QPromiseReject<T>& reject,
const Sender* sender)
{
connections << QObject::connect(sender, &QObject::destroyed, [=]() {
connections.disconnect();
reject(QtPromise::QPromiseContextException());
});
}
} // namespace QtPromisePrivate
#endif // QTPROMISE_QPROMISEHELPERS_P_H

View File

@ -0,0 +1,142 @@
#ifndef QTPROMISE_QPROMISERESOLVER_H
#define QTPROMISE_QPROMISERESOLVER_H
#include "qpromiseexceptions.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();
}
}
void reject()
{
auto promise = m_d->promise;
if (promise) {
Q_ASSERT(promise->isPending());
promise->m_d->reject(QtPromise::QPromiseUndefinedException());
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));
}
void operator()() const
{
m_resolver.reject();
}
private:
mutable QtPromisePrivate::PromiseResolver<T> m_resolver;
};
} // namespace QtPromise
#endif // QTPROMISE_QPROMISERESOLVER_H

View File

@ -2,7 +2,10 @@ HEADERS += \
$$PWD/qpromise.h \
$$PWD/qpromise.inl \
$$PWD/qpromise_p.h \
$$PWD/qpromiseerror.h \
$$PWD/qpromiseconnections.h \
$$PWD/qpromiseexceptions.h \
$$PWD/qpromisefuture.h \
$$PWD/qpromiseglobal.h \
$$PWD/qpromisehelpers.h
$$PWD/qpromisehelpers.h \
$$PWD/qpromisehelpers_p.h \
$$PWD/qpromiseresolver.h

View File

@ -1,3 +1,5 @@
#include "../shared/data.h"
// QtPromise
#include <QtPromise>
@ -20,7 +22,6 @@ class tst_benchmark : public QObject
private Q_SLOTS:
void valueResolve();
void valueResolveStatic();
void valueReject();
void valueThen();
void valueFinally();
@ -34,72 +35,6 @@ private Q_SLOTS:
QTEST_MAIN(tst_benchmark)
#include "tst_benchmark.moc"
struct Logs {
int ctor = 0;
int copy = 0;
int move = 0;
int refs = 0;
void reset() {
ctor = 0;
copy = 0;
move = 0;
refs = 0;
}
};
struct Logger
{
Logger() { logs().ctor++; logs().refs++; }
Logger(const Logger&) { logs().copy++; logs().refs++; }
Logger(Logger&&) { logs().move++; logs().refs++; }
~Logger() { logs().refs--; }
Logger& operator=(const Logger&) { logs().copy++; return *this; }
Logger& operator=(Logger&&) { logs().move++; return *this; }
public: // STATICS
static Logs& logs() { static Logs logs; return logs; }
};
struct Data : public Logger
{
Data(int v): Logger(), m_value(v) {}
int value() const { return m_value; }
// MSVC 2013 doesn't support implicit generation of the move constructor and
// operator, so we need to explicitly define these methods and thus the copy
// constructor and operator also need to be explicitly defined (error C2280).
// https://stackoverflow.com/a/26581337
Data(const Data& other)
: Logger(other)
, m_value(other.m_value)
{ }
Data(Data&& other) : Logger(std::forward<Data>(other))
{
qSwap(m_value, other.m_value);
}
Data& operator=(const Data& other)
{
Logger::operator=(other);
m_value = other.m_value;
return *this;
}
Data& operator=(Data&& other)
{
Logger::operator=(std::forward<Data>(other));
qSwap(m_value, other.m_value);
return *this;
}
private:
int m_value;
};
void tst_benchmark::valueResolve()
{
{ // should move the value when resolved by rvalue
@ -127,31 +62,6 @@ void tst_benchmark::valueResolve()
}
}
void tst_benchmark::valueResolveStatic()
{
{ // should move the value when resolved by rvalue
Data::logs().reset();
QPromise<Data>::resolve(Data(42)).wait();
QCOMPARE(Data::logs().ctor, 1);
QCOMPARE(Data::logs().copy, 0);
QCOMPARE(Data::logs().move, 1); // move value to the promise data
QCOMPARE(Data::logs().refs, 0);
}
{ // should create one copy of the value when resolved by lvalue
{
Data::logs().reset();
Data value(42);
QPromise<Data>::resolve(value).wait();
}
QCOMPARE(Data::logs().ctor, 1);
QCOMPARE(Data::logs().copy, 1); // copy value to the promise data
QCOMPARE(Data::logs().move, 0);
QCOMPARE(Data::logs().refs, 0);
}
}
void tst_benchmark::valueReject()
{
{ // should not create any data if rejected

View File

@ -0,0 +1,5 @@
include(../qtpromise.pri)
DEFINES -= QT_DEPRECATED_WARNINGS
gcc:QMAKE_CXXFLAGS += -Wno-deprecated-declarations
msvc:QMAKE_CXXFLAGS -= -wd4996

View File

@ -0,0 +1,3 @@
TEMPLATE = subdirs
SUBDIRS += \
helpers

View File

@ -0,0 +1,4 @@
TEMPLATE = subdirs
SUBDIRS += \
qpromise \
qpromiseall

View File

@ -0,0 +1,5 @@
QT += concurrent
TARGET = tst_deprecations_helpers_qpromise
SOURCES += $$PWD/tst_qpromise.cpp
include(../../deprecations.pri)

View File

@ -0,0 +1,268 @@
#include "../../../shared/data.h"
#include "../../../shared/utils.h"
// QtPromise
#include <QtPromise>
// Qt
#include <QtConcurrent>
#include <QtTest>
// STL
#include <memory>
using namespace QtPromise;
class tst_deprecations_helpers_qpromise : public QObject
{
Q_OBJECT
private Q_SLOTS:
void value();
void noValue();
void moveRValue();
void copyLValue();
void qtSharedPtr();
void stdSharedPtr();
void typedPromise();
void voidPromise();
void typedFuture();
void voidFuture();
};
QTEST_MAIN(tst_deprecations_helpers_qpromise)
#include "tst_qpromise.moc"
void tst_deprecations_helpers_qpromise::value()
{
int v0 = 42;
const int v1 = 42;
auto p0 = qPromise(42);
auto p1 = qPromise(v0);
auto p2 = qPromise(v1);
Q_STATIC_ASSERT((std::is_same<decltype(p0), QPromise<int>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1), QPromise<int>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p2), QPromise<int>>::value));
for (const auto& p : {p0, p1, p2}) {
QCOMPARE(p.isFulfilled(), true);
}
for (const auto& p : {p0, p1, p2}) {
QCOMPARE(waitForValue(p, -1), 42);
}
}
void tst_deprecations_helpers_qpromise::noValue()
{
auto p = qPromise();
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
QCOMPARE(p.isFulfilled(), true);
QCOMPARE(waitForValue(p, -1, 42), 42);
}
void tst_deprecations_helpers_qpromise::moveRValue()
{
Data::logs().reset();
{
auto p = qPromise(Data(42)).wait();
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<Data>>::value));
}
QCOMPARE(Data::logs().ctor, 1);
QCOMPARE(Data::logs().copy, 0);
QCOMPARE(Data::logs().move, 1);
QCOMPARE(Data::logs().refs, 0);
}
void tst_deprecations_helpers_qpromise::copyLValue()
{
Data::logs().reset();
{
Data value(42);
auto p = qPromise(value).wait();
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<Data>>::value));
}
QCOMPARE(Data::logs().ctor, 1);
QCOMPARE(Data::logs().copy, 1);
QCOMPARE(Data::logs().move, 0);
QCOMPARE(Data::logs().refs, 0);
}
// https://github.com/simonbrunel/qtpromise/issues/6
void tst_deprecations_helpers_qpromise::qtSharedPtr()
{
Data::logs().reset();
QWeakPointer<Data> wptr;
{
QSharedPointer<Data> sptr0(new Data(42));
const QSharedPointer<Data> sptr1 = sptr0;
auto p0 = qPromise(QSharedPointer<Data>(new Data(42)));
auto p1 = qPromise(sptr0);
auto p2 = qPromise(sptr1);
Q_STATIC_ASSERT((std::is_same<decltype(p0), QPromise<QSharedPointer<Data>>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1), QPromise<QSharedPointer<Data>>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p2), QPromise<QSharedPointer<Data>>>::value));
QCOMPARE(waitForValue(p1, QSharedPointer<Data>()), sptr0);
QCOMPARE(waitForValue(p2, QSharedPointer<Data>()), sptr1);
wptr = sptr0;
QCOMPARE(wptr.isNull(), false);
QCOMPARE(Data::logs().refs, 2);
}
QCOMPARE(wptr.isNull(), true);
QCOMPARE(Data::logs().ctor, 2);
QCOMPARE(Data::logs().copy, 0);
QCOMPARE(Data::logs().move, 0);
QCOMPARE(Data::logs().refs, 0);
}
// https://github.com/simonbrunel/qtpromise/issues/6
void tst_deprecations_helpers_qpromise::stdSharedPtr()
{
Data::logs().reset();
std::weak_ptr<Data> wptr;
{
std::shared_ptr<Data> sptr0(new Data(42));
const std::shared_ptr<Data> sptr1 = sptr0;
auto p0 = qPromise(std::shared_ptr<Data>(new Data(42)));
auto p1 = qPromise(sptr0);
auto p2 = qPromise(sptr1);
Q_STATIC_ASSERT((std::is_same<decltype(p0), QPromise<std::shared_ptr<Data>>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1), QPromise<std::shared_ptr<Data>>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p2), QPromise<std::shared_ptr<Data>>>::value));
QCOMPARE(waitForValue(p1, std::shared_ptr<Data>()), sptr0);
QCOMPARE(waitForValue(p2, std::shared_ptr<Data>()), sptr1);
wptr = sptr0;
QCOMPARE(wptr.use_count(), 4l);
QCOMPARE(Data::logs().refs, 2);
}
QCOMPARE(wptr.use_count(), 0l);
QCOMPARE(Data::logs().ctor, 2);
QCOMPARE(Data::logs().copy, 0);
QCOMPARE(Data::logs().move, 0);
QCOMPARE(Data::logs().refs, 0);
}
void tst_deprecations_helpers_qpromise::typedPromise()
{
auto resolver = [](const QPromiseResolve<int>& resolve) {
QtPromisePrivate::qtpromise_defer([=](){
resolve(42);
});
};
QPromise<int> v0(resolver);
const QPromise<int> v1 = v0;
auto p0 = qPromise(QPromise<int>(resolver));
auto p1 = qPromise(v0);
auto p2 = qPromise(v1);
Q_STATIC_ASSERT((std::is_same<decltype(p0), QPromise<int>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1), QPromise<int>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p2), QPromise<int>>::value));
for (const auto& promise : {p0, p1, p2}) {
QCOMPARE(promise.isPending(), true);
}
for (const auto& promise : {p0, p1, p2}) {
QCOMPARE(waitForValue(promise, -1), 42);
}
}
void tst_deprecations_helpers_qpromise::voidPromise()
{
auto resolver = [](const QPromiseResolve<void>& resolve) {
QtPromisePrivate::qtpromise_defer([=](){
resolve();
});
};
QPromise<void> v0(resolver);
const QPromise<void> v1 = v0;
auto p0 = qPromise(QPromise<void>(resolver));
auto p1 = qPromise(v0);
auto p2 = qPromise(v1);
Q_STATIC_ASSERT((std::is_same<decltype(p0), QPromise<void>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1), QPromise<void>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p2), QPromise<void>>::value));
for (const auto& promise : {p0, p1, p2}) {
QCOMPARE(promise.isPending(), true);
}
for (const auto& promise : {p0, p1, p2}) {
QCOMPARE(waitForValue(promise, -1, 42), 42);
}
}
void tst_deprecations_helpers_qpromise::typedFuture()
{
auto fn = [](){ return 42; };
QFuture<int> v0 = QtConcurrent::run(fn);
const QFuture<int> v1 = v0;
auto p0 = qPromise(QtConcurrent::run(fn));
auto p1 = qPromise(v0);
auto p2 = qPromise(v1);
Q_STATIC_ASSERT((std::is_same<decltype(p0), QPromise<int>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1), QPromise<int>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p2), QPromise<int>>::value));
for (const auto& promise : {p0, p1, p2}) {
QCOMPARE(promise.isPending(), true);
}
for (const auto& promise : {p0, p1, p2}) {
QCOMPARE(waitForValue(promise, -1), 42);
}
}
void tst_deprecations_helpers_qpromise::voidFuture()
{
auto fn = [](){ };
QFuture<void> v0 = QtConcurrent::run(fn);
const QFuture<void> v1 = v0;
auto p0 = qPromise(QtConcurrent::run(fn));
auto p1 = qPromise(v0);
auto p2 = qPromise(v1);
Q_STATIC_ASSERT((std::is_same<decltype(p0), QPromise<void>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1), QPromise<void>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p2), QPromise<void>>::value));
for (const auto& promise : {p0, p1, p2}) {
QCOMPARE(promise.isPending(), true);
}
for (const auto& promise : {p0, p1, p2}) {
QCOMPARE(waitForValue(promise, -1, 42), 42);
}
}

View File

@ -0,0 +1,4 @@
TARGET = tst_deprecations_helpers_qpromiseall
SOURCES += $$PWD/tst_qpromiseall.cpp
include(../../deprecations.pri)

View File

@ -0,0 +1,220 @@
#include "../../../shared/utils.h"
// QtPromise
#include <QtPromise>
// Qt
#include <QtTest>
using namespace QtPromise;
class tst_deprecations_helpers_qpromiseall : 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_deprecations_helpers_qpromiseall)
#include "tst_qpromiseall.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::resolve(42),
QtPromise::resolve(43),
QtPromise::resolve(44)
};
promises.push_back(QtPromise::resolve(45));
promises.insert(++promises.begin(), QtPromise::resolve(46));
promises.pop_back();
auto p = 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::resolve(),
QtPromise::resolve(),
QtPromise::resolve()
};
promises.push_back(QtPromise::resolve());
promises.insert(++promises.begin(), QtPromise::resolve());
promises.pop_back();
auto p = 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_deprecations_helpers_qpromiseall::emptySequence()
{
auto p = 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_deprecations_helpers_qpromiseall::emptySequence_void()
{
auto p = 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_deprecations_helpers_qpromiseall::allPromisesSucceed()
{
auto p0 = QtPromise::resolve(42);
auto p1 = QtPromise::resolve(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(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_deprecations_helpers_qpromiseall::allPromisesSucceed_void()
{
auto p0 = QtPromise::resolve();
auto p1 = QtPromise::resolve();
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(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_deprecations_helpers_qpromiseall::atLeastOnePromiseReject()
{
auto p0 = QtPromise::resolve(42);
auto p1 = QtPromise::resolve(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(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_deprecations_helpers_qpromiseall::atLeastOnePromiseReject_void()
{
auto p0 = QtPromise::resolve();
auto p1 = QtPromise::resolve();
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(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_deprecations_helpers_qpromiseall::preserveOrder()
{
auto p0 = QtPromise::resolve(42).delay(500);
auto p1 = QtPromise::resolve(43).delay(100);
auto p2 = QtPromise::resolve(44).delay(250);
auto p = 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_deprecations_helpers_qpromiseall::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_deprecations_helpers_qpromiseall::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,4 @@
TARGET = tst_deprecations_qpromise_all
SOURCES += $$PWD/tst_all.cpp
include(../../deprecations.pri)

View File

@ -0,0 +1,229 @@
#include "../../../shared/utils.h"
// QtPromise
#include <QtPromise>
// Qt
#include <QtTest>
using namespace QtPromise;
class tst_deprecations_qpromise_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_deprecations_qpromise_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::resolve(42),
QtPromise::resolve(43),
QtPromise::resolve(44)
};
promises.push_back(QtPromise::resolve(45));
promises.insert(++promises.begin(), QtPromise::resolve(46));
promises.pop_back();
auto p = QPromise<int>::all(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::resolve(),
QtPromise::resolve(),
QtPromise::resolve()
};
promises.push_back(QtPromise::resolve());
promises.insert(++promises.begin(), QtPromise::resolve());
promises.pop_back();
auto p = QPromise<void>::all(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_deprecations_qpromise_all::emptySequence()
{
auto p = QPromise<int>::all({});
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_deprecations_qpromise_all::emptySequence_void()
{
auto p = QPromise<void>::all({});
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
QCOMPARE(p.isFulfilled(), true);
QCOMPARE(waitForValue(p, -1, 42), 42);
}
void tst_deprecations_qpromise_all::allPromisesSucceed()
{
auto p0 = QtPromise::resolve(42);
auto p1 = QtPromise::resolve(44);
auto p2 = QPromise<int>([](const QPromiseResolve<int>& resolve) {
QtPromisePrivate::qtpromise_defer([=](){
resolve(43);
});
});
auto p = QPromise<int>::all({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_deprecations_qpromise_all::allPromisesSucceed_void()
{
auto p0 = QtPromise::resolve();
auto p1 = QtPromise::resolve();
auto p2 = QPromise<void>([](const QPromiseResolve<void>& resolve) {
QtPromisePrivate::qtpromise_defer([=](){
resolve();
});
});
auto p = QPromise<void>::all({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_deprecations_qpromise_all::atLeastOnePromiseReject()
{
auto p0 = QtPromise::resolve(42);
auto p1 = QtPromise::resolve(44);
auto p2 = QPromise<int>([](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
QtPromisePrivate::qtpromise_defer([=](){
reject(QString("foo"));
});
});
auto p = QPromise<int>::all({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_deprecations_qpromise_all::atLeastOnePromiseReject_void()
{
auto p0 = QtPromise::resolve();
auto p1 = QtPromise::resolve();
auto p2 = QPromise<void>([](const QPromiseResolve<void>&, const QPromiseReject<void>& reject) {
QtPromisePrivate::qtpromise_defer([=](){
reject(QString("foo"));
});
});
auto p = QPromise<void>::all({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_deprecations_qpromise_all::preserveOrder()
{
auto p0 = QtPromise::resolve(42).delay(500);
auto p1 = QtPromise::resolve(43).delay(100);
auto p2 = QtPromise::resolve(44).delay(250);
auto p = QPromise<int>::all({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_deprecations_qpromise_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_deprecations_qpromise_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,3 @@
TEMPLATE = subdirs
SUBDIRS += \
all

View File

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

View File

@ -0,0 +1,58 @@
#include "../shared/utils.h"
// QtPromise
#include <QtPromise>
// Qt
#include <QtConcurrent>
#include <QtTest>
using namespace QtPromise;
class tst_exceptions : public QObject
{
Q_OBJECT
private Q_SLOTS:
void canceled();
void context();
void timeout();
void undefined();
}; // class tst_exceptions
QTEST_MAIN(tst_exceptions)
#include "tst_exceptions.moc"
namespace {
template <class E>
void verify()
{
auto p = QtPromise::resolve(QtConcurrent::run([]() { throw E(); }));
QCOMPARE(p.isPending(), true);
QCOMPARE(waitForRejected<E>(p), true);
QCOMPARE(p.isRejected(), true);
}
} // anonymous namespace
void tst_exceptions::canceled()
{
verify<QPromiseCanceledException>();
}
void tst_exceptions::context()
{
verify<QPromiseContextException>();
}
void tst_exceptions::timeout()
{
verify<QPromiseTimeoutException>();
}
void tst_exceptions::undefined()
{
verify<QPromiseUndefinedException>();
}

View File

@ -52,7 +52,7 @@ QTEST_MAIN(tst_future)
void tst_future::fulfilled()
{
int result = -1;
auto p = qPromise(QtConcurrent::run([]() {
auto p = QtPromise::resolve(QtConcurrent::run([]() {
return 42;
}));
@ -70,7 +70,7 @@ void tst_future::fulfilled()
void tst_future::fulfilled_void()
{
int result = -1;
auto p = qPromise(QtConcurrent::run([]() { }));
auto p = QtPromise::resolve(QtConcurrent::run([]() { }));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
QCOMPARE(p.isPending(), true);
@ -86,7 +86,7 @@ void tst_future::fulfilled_void()
void tst_future::rejected()
{
QString error;
auto p = qPromise(QtConcurrent::run([]() {
auto p = QtPromise::resolve(QtConcurrent::run([]() {
throw MyException("foo");
return 42;
}));
@ -106,7 +106,7 @@ void tst_future::rejected()
void tst_future::rejected_void()
{
QString error;
auto p = qPromise(QtConcurrent::run([]() {
auto p = QtPromise::resolve(QtConcurrent::run([]() {
throw MyException("foo");
}));
@ -125,7 +125,7 @@ void tst_future::rejected_void()
void tst_future::unhandled()
{
QString error;
auto p = qPromise(QtConcurrent::run([]() {
auto p = QtPromise::resolve(QtConcurrent::run([]() {
throw QString("foo");
return 42;
}));
@ -149,7 +149,7 @@ void tst_future::unhandled()
void tst_future::unhandled_void()
{
QString error;
auto p = qPromise(QtConcurrent::run([]() {
auto p = QtPromise::resolve(QtConcurrent::run([]() {
throw QString("foo");
}));
@ -169,7 +169,7 @@ void tst_future::unhandled_void()
void tst_future::canceled()
{
QString error;
auto p = qPromise(QFuture<int>()); // Constructs an empty, canceled future.
auto p = QtPromise::resolve(QFuture<int>()); // Constructs an empty, canceled future.
QCOMPARE(p.isPending(), true);
@ -185,7 +185,7 @@ void tst_future::canceled()
void tst_future::canceled_void()
{
QString error;
auto p = qPromise(QFuture<void>()); // Constructs an empty, canceled future.
auto p = QtPromise::resolve(QFuture<void>()); // Constructs an empty, canceled future.
QCOMPARE(p.isPending(), true);
@ -200,7 +200,7 @@ void tst_future::canceled_void()
void tst_future::canceledFromThread()
{
QString error;
auto p = qPromise(QtConcurrent::run([]() {
auto p = QtPromise::resolve(QtConcurrent::run([]() {
throw QPromiseCanceledException();
}));
@ -217,7 +217,7 @@ void tst_future::canceledFromThread()
void tst_future::then()
{
QString result;
auto input = qPromise(42);
auto input = QtPromise::resolve(42);
auto output = input.then([](int res) {
return QtConcurrent::run([=]() {
return QString("foo%1").arg(res);
@ -238,7 +238,7 @@ void tst_future::then()
void tst_future::then_void()
{
QString result;
auto input = qPromise();
auto input = QtPromise::resolve();
auto output = input.then([&]() {
return QtConcurrent::run([&]() {
result = "foo";
@ -300,7 +300,7 @@ void tst_future::fail_void()
void tst_future::finally()
{
auto input = qPromise(42);
auto input = QtPromise::resolve(42);
auto output = input.finally([]() {
return QtConcurrent::run([]() {
return QString("foo");
@ -323,7 +323,7 @@ void tst_future::finally()
void tst_future::finallyRejected()
{
auto input = qPromise(42);
auto input = QtPromise::resolve(42);
auto output = input.finally([]() {
return QtConcurrent::run([]() {
throw MyException("foo");

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,220 @@
#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::resolve(42),
QtPromise::resolve(43),
QtPromise::resolve(44)
};
promises.push_back(QtPromise::resolve(45));
promises.insert(++promises.begin(), QtPromise::resolve(46));
promises.pop_back();
auto p = QtPromise::all(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::resolve(),
QtPromise::resolve(),
QtPromise::resolve()
};
promises.push_back(QtPromise::resolve());
promises.insert(++promises.begin(), QtPromise::resolve());
promises.pop_back();
auto p = QtPromise::all(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::all(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::all(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::resolve(42);
auto p1 = QtPromise::resolve(44);
auto p2 = QPromise<int>([](const QPromiseResolve<int>& resolve) {
QtPromisePrivate::qtpromise_defer([=](){
resolve(43);
});
});
auto p = QtPromise::all(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::resolve();
auto p1 = QtPromise::resolve();
auto p2 = QPromise<void>([](const QPromiseResolve<void>& resolve) {
QtPromisePrivate::qtpromise_defer([=](){
resolve();
});
});
auto p = QtPromise::all(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::resolve(42);
auto p1 = QtPromise::resolve(44);
auto p2 = QPromise<int>([](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
QtPromisePrivate::qtpromise_defer([=](){
reject(QString("foo"));
});
});
auto p = QtPromise::all(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::resolve();
auto p1 = QtPromise::resolve();
auto p2 = QPromise<void>([](const QPromiseResolve<void>&, const QPromiseReject<void>& reject) {
QtPromisePrivate::qtpromise_defer([=](){
reject(QString("foo"));
});
});
auto p = QtPromise::all(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::resolve(42).delay(500);
auto p1 = QtPromise::resolve(43).delay(100);
auto p2 = QtPromise::resolve(44).delay(250);
auto p = QtPromise::all(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,98 @@
#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::resolve(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_helpers_connect
SOURCES += $$PWD/tst_connect.cpp
include(../../qtpromise.pri)

View File

@ -0,0 +1,211 @@
#include "../../shared/object.h"
#include "../../shared/utils.h"
// QtPromise
#include <QtPromise>
// Qt
#include <QtTest>
using namespace QtPromise;
class tst_helpers_connect : public QObject
{
Q_OBJECT
private Q_SLOTS:
// connect(QObject* sender, Signal resolver)
void resolveOneSenderNoArg();
void resolveOneSenderOneArg();
void resolveOneSenderManyArgs();
// connect(QObject* sender, Signal resolver, Signal rejecter)
void rejectOneSenderNoArg();
void rejectOneSenderOneArg();
void rejectOneSenderManyArgs();
void rejectOneSenderDestroyed();
// connect(QObject* s0, Signal resolver, QObject* s1, Signal rejecter)
void rejectTwoSendersNoArg();
void rejectTwoSendersOneArg();
void rejectTwoSendersManyArgs();
void rejectTwoSendersDestroyed();
};
QTEST_MAIN(tst_helpers_connect)
#include "tst_connect.moc"
void tst_helpers_connect::resolveOneSenderNoArg()
{
Object sender;
QtPromisePrivate::qtpromise_defer([&]() {
Q_EMIT sender.noArgSignal();
});
auto p = QtPromise::connect(&sender, &Object::noArgSignal);
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
QCOMPARE(sender.hasConnections(), true);
QCOMPARE(p.isPending(), true);
QCOMPARE(waitForValue(p, -1, 42), 42);
QCOMPARE(sender.hasConnections(), false);
}
void tst_helpers_connect::resolveOneSenderOneArg()
{
Object sender;
QtPromisePrivate::qtpromise_defer([&]() {
Q_EMIT sender.oneArgSignal("foo");
});
auto p = QtPromise::connect(&sender, &Object::oneArgSignal);
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QString>>::value));
QCOMPARE(sender.hasConnections(), true);
QCOMPARE(p.isPending(), true);
QCOMPARE(waitForValue(p, QString()), QString("foo"));
QCOMPARE(sender.hasConnections(), false);
}
void tst_helpers_connect::resolveOneSenderManyArgs()
{
Object sender;
QtPromisePrivate::qtpromise_defer([&]() {
Q_EMIT sender.twoArgsSignal(42, "foo");
});
auto p = QtPromise::connect(&sender, &Object::twoArgsSignal);
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int>>::value));
QCOMPARE(sender.hasConnections(), true);
QCOMPARE(p.isPending(), true);
QCOMPARE(waitForValue(p, -1), 42);
QCOMPARE(sender.hasConnections(), false);
}
void tst_helpers_connect::rejectOneSenderNoArg()
{
Object sender;
QtPromisePrivate::qtpromise_defer([&]() {
Q_EMIT sender.noArgSignal();
});
auto p = QtPromise::connect(&sender, &Object::oneArgSignal, &Object::noArgSignal);
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QString>>::value));
QCOMPARE(sender.hasConnections(), true);
QCOMPARE(p.isPending(), true);
QCOMPARE(waitForRejected<QPromiseUndefinedException>(p), true);
QCOMPARE(sender.hasConnections(), false);
}
void tst_helpers_connect::rejectOneSenderOneArg()
{
Object sender;
QtPromisePrivate::qtpromise_defer([&]() {
Q_EMIT sender.oneArgSignal("bar");
});
auto p = QtPromise::connect(&sender, &Object::noArgSignal, &Object::oneArgSignal);
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
QCOMPARE(sender.hasConnections(), true);
QCOMPARE(p.isPending(), true);
QCOMPARE(waitForError(p, QString()), QString("bar"));
QCOMPARE(sender.hasConnections(), false);
}
void tst_helpers_connect::rejectOneSenderManyArgs()
{
Object sender;
QtPromisePrivate::qtpromise_defer([&]() {
Q_EMIT sender.twoArgsSignal(42, "bar");
});
auto p = QtPromise::connect(&sender, &Object::noArgSignal, &Object::twoArgsSignal);
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
QCOMPARE(sender.hasConnections(), true);
QCOMPARE(p.isPending(), true);
QCOMPARE(waitForError(p, -1), 42);
QCOMPARE(sender.hasConnections(), false);
}
void tst_helpers_connect::rejectOneSenderDestroyed()
{
Object* sender = new Object();
QtPromisePrivate::qtpromise_defer([&]() {
sender->deleteLater();
});
auto p = QtPromise::connect(sender, &Object::twoArgsSignal);
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int>>::value));
QCOMPARE(p.isPending(), true);
QCOMPARE(waitForRejected<QPromiseContextException>(p), true);
}
void tst_helpers_connect::rejectTwoSendersNoArg()
{
Object s0, s1;
QtPromisePrivate::qtpromise_defer([&]() {
Q_EMIT s1.noArgSignal();
});
auto p = QtPromise::connect(&s0, &Object::noArgSignal, &s1, &Object::noArgSignal);
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
QCOMPARE(s0.hasConnections(), true);
QCOMPARE(s1.hasConnections(), true);
QCOMPARE(p.isPending(), true);
QCOMPARE(waitForRejected<QPromiseUndefinedException>(p), true);
QCOMPARE(s0.hasConnections(), false);
QCOMPARE(s1.hasConnections(), false);
}
void tst_helpers_connect::rejectTwoSendersOneArg()
{
Object s0, s1;
QtPromisePrivate::qtpromise_defer([&]() {
Q_EMIT s1.oneArgSignal("bar");
});
auto p = QtPromise::connect(&s0, &Object::noArgSignal, &s1, &Object::oneArgSignal);
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
QCOMPARE(s0.hasConnections(), true);
QCOMPARE(s1.hasConnections(), true);
QCOMPARE(p.isPending(), true);
QCOMPARE(waitForError(p, QString()), QString("bar"));
QCOMPARE(s0.hasConnections(), false);
QCOMPARE(s1.hasConnections(), false);
}
void tst_helpers_connect::rejectTwoSendersManyArgs()
{
Object s0, s1;
QtPromisePrivate::qtpromise_defer([&]() {
Q_EMIT s1.twoArgsSignal(42, "bar");
});
auto p = QtPromise::connect(&s0, &Object::noArgSignal, &s1, &Object::twoArgsSignal);
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
QCOMPARE(s0.hasConnections(), true);
QCOMPARE(s1.hasConnections(), true);
QCOMPARE(p.isPending(), true);
QCOMPARE(waitForError(p, -1), 42);
QCOMPARE(s0.hasConnections(), false);
QCOMPARE(s1.hasConnections(), false);
}
void tst_helpers_connect::rejectTwoSendersDestroyed()
{
Object* s0 = new Object();
Object* s1 = new Object();
QtPromisePrivate::qtpromise_defer([&]() {
QObject::connect(s1, &QObject::destroyed, [&]() {
// Let's first delete s1, then resolve s0 and make sure
// we don't reject when the rejecter object is destroyed.
Q_EMIT s0->noArgSignal();
});
s1->deleteLater();
});
auto p = QtPromise::connect(s0, &Object::noArgSignal, s1, &Object::twoArgsSignal);
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
QCOMPARE(p.isPending(), true);
QCOMPARE(waitForValue(p, -1, 42), 42);
}

View File

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

View File

@ -0,0 +1,153 @@
#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,146 @@
#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,11 @@
TARGET = tst_helpers
SOURCES += $$PWD/tst_helpers.cpp
include(../qtpromise.pri)
TEMPLATE = subdirs
SUBDIRS += \
all \
attempt \
connect \
each \
filter \
map \
reduce \
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,150 @@
#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

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

View File

@ -0,0 +1,271 @@
#include "../../shared/data.h"
#include "../../shared/utils.h"
// QtPromise
#include <QtPromise>
// Qt
#include <QtTest>
using namespace QtPromise;
class tst_helpers_reduce : public QObject
{
Q_OBJECT
private Q_SLOTS:
void emptySequence();
void regularValues();
void promiseValues();
void convertResultType();
void delayedInitialValue();
void delayedFulfilled();
void delayedRejected();
void functorThrows();
void sequenceTypes();
};
QTEST_MAIN(tst_helpers_reduce)
#include "tst_reduce.moc"
namespace {
template <class Sequence>
struct SequenceTester
{
static void exec()
{
Sequence inputs{
QtPromise::resolve(4).delay(400),
QtPromise::resolve(6).delay(300),
QtPromise::resolve(8).delay(200)
};
QVector<int> v0;
QVector<int> v1;
auto p0 = QtPromise::reduce(inputs, [&](int acc, int cur, int idx) {
v0 << acc << cur << idx;
return acc + cur + idx;
});
auto p1 = QtPromise::reduce(inputs, [&](int acc, int cur, int idx) {
v1 << acc << cur << idx;
return acc + cur + idx;
}, QtPromise::resolve(2).delay(100));
Q_STATIC_ASSERT((std::is_same<decltype(p0), QPromise<int>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1), QPromise<int>>::value));
QCOMPARE(p0.isPending(), true);
QCOMPARE(p1.isPending(), true);
QCOMPARE(waitForValue(p0, -1), 21);
QCOMPARE(waitForValue(p1, -1), 23);
QCOMPARE(v0, QVector<int>({4, 6, 1, 11, 8, 2}));
QCOMPARE(v1, QVector<int>({2, 4, 0, 6, 6, 1, 13, 8, 2}));
}
};
} // anonymous namespace
void tst_helpers_reduce::emptySequence()
{
bool called = false;
auto p = QtPromise::reduce(QVector<int>{}, [&](...) {
called = true;
return 43;
}, 42);
// NOTE(SB): reduce() on an empty sequence without an initial value is an error!
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int>>::value));
QCOMPARE(waitForValue(p, -1), 42);
QCOMPARE(called, false);
}
void tst_helpers_reduce::regularValues()
{
QVector<int> inputs{4, 6, 8};
QVector<int> v0;
QVector<int> v1;
auto p0 = QtPromise::reduce(inputs, [&](int acc, int cur, int idx) {
v0 << acc << cur << idx;
return acc + cur + idx;
});
auto p1 = QtPromise::reduce(inputs, [&](int acc, int cur, int idx) {
v1 << acc << cur << idx;
return acc + cur + idx;
}, 2);
Q_STATIC_ASSERT((std::is_same<decltype(p0), QPromise<int>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1), QPromise<int>>::value));
QCOMPARE(p0.isPending(), true);
QCOMPARE(p1.isPending(), true);
QCOMPARE(waitForValue(p0, -1), 21);
QCOMPARE(waitForValue(p1, -1), 23);
QCOMPARE(v0, QVector<int>({4, 6, 1, 11, 8, 2}));
QCOMPARE(v1, QVector<int>({2, 4, 0, 6, 6, 1, 13, 8, 2}));
}
void tst_helpers_reduce::promiseValues()
{
QVector<QPromise<int>> inputs{
QtPromise::resolve(4).delay(400),
QtPromise::resolve(6).delay(300),
QtPromise::resolve(8).delay(200)
};
QVector<int> v0;
QVector<int> v1;
auto p0 = QtPromise::reduce(inputs, [&](int acc, int cur, int idx) {
v0 << acc << cur << idx;
return acc + cur + idx;
});
auto p1 = QtPromise::reduce(inputs, [&](int acc, int cur, int idx) {
v1 << acc << cur << idx;
return acc + cur + idx;
}, 2);
Q_STATIC_ASSERT((std::is_same<decltype(p0), QPromise<int>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1), QPromise<int>>::value));
QCOMPARE(p0.isPending(), true);
QCOMPARE(p1.isPending(), true);
QCOMPARE(waitForValue(p0, -1), 21);
QCOMPARE(waitForValue(p1, -1), 23);
QCOMPARE(v0, QVector<int>({4, 6, 1, 11, 8, 2}));
QCOMPARE(v1, QVector<int>({2, 4, 0, 6, 6, 1, 13, 8, 2}));
}
void tst_helpers_reduce::convertResultType()
{
QVector<int> inputs{4, 6, 8};
auto p = QtPromise::reduce(inputs, [&](const QString& acc, int cur, int idx) {
return QString("%1:%2:%3").arg(acc).arg(cur).arg(idx);
}, QString("foo"));
// NOTE(SB): when no initial value is given, the result type is the sequence type.
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QString>>::value));
QCOMPARE(p.isPending(), true);
QCOMPARE(waitForValue(p, QString()), QString("foo:4:0:6:1:8:2"));
}
void tst_helpers_reduce::delayedInitialValue()
{
QVector<int> values;
auto p = QtPromise::reduce(QVector<int>{4, 6, 8}, [&](int acc, int cur, int idx) {
values << acc << cur << idx;
return acc + cur + idx;
}, QtPromise::resolve(2).delay(100));
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int>>::value));
QCOMPARE(p.isPending(), true);
QCOMPARE(waitForValue(p, -1), 23);
QCOMPARE(values, QVector<int>({2, 4, 0, 6, 6, 1, 13, 8, 2}));
}
void tst_helpers_reduce::delayedFulfilled()
{
QVector<int> inputs{4, 6, 8};
QVector<int> v0;
QVector<int> v1;
auto p0 = QtPromise::reduce(inputs, [&](int acc, int cur, int idx) {
v0 << acc << cur << idx;
return QtPromise::resolve(acc + cur + idx).delay(100);
});
auto p1 = QtPromise::reduce(inputs, [&](int acc, int cur, int idx) {
v1 << acc << cur << idx;
return QtPromise::resolve(acc + cur + idx).delay(100);
}, 2);
Q_STATIC_ASSERT((std::is_same<decltype(p0), QPromise<int>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1), QPromise<int>>::value));
QCOMPARE(p0.isPending(), true);
QCOMPARE(p1.isPending(), true);
QCOMPARE(waitForValue(p0, -1), 21);
QCOMPARE(waitForValue(p1, -1), 23);
QCOMPARE(v0, QVector<int>({4, 6, 1, 11, 8, 2}));
QCOMPARE(v1, QVector<int>({2, 4, 0, 6, 6, 1, 13, 8, 2}));
}
void tst_helpers_reduce::delayedRejected()
{
QVector<int> inputs{4, 6, 8};
QVector<int> v0;
QVector<int> v1;
auto p0 = QtPromise::reduce(inputs, [&](int acc, int cur, int idx) {
v0 << acc << cur << idx;
if (cur == 6) {
return QPromise<int>::reject(QString("foo"));
}
return QtPromise::resolve(acc + cur + idx);
});
auto p1 = QtPromise::reduce(inputs, [&](int acc, int cur, int idx) {
v1 << acc << cur << idx;
if (cur == 6) {
return QPromise<int>::reject(QString("bar"));
}
return QtPromise::resolve(acc + cur + idx);
}, 2);
Q_STATIC_ASSERT((std::is_same<decltype(p0), QPromise<int>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1), QPromise<int>>::value));
QCOMPARE(p0.isPending(), true);
QCOMPARE(p1.isPending(), true);
QCOMPARE(waitForError(p0, QString()), QString("foo"));
QCOMPARE(waitForError(p1, QString()), QString("bar"));
QCOMPARE(v0, QVector<int>({4, 6, 1}));
QCOMPARE(v1, QVector<int>({2, 4, 0, 6, 6, 1}));
}
void tst_helpers_reduce::functorThrows()
{
QVector<int> inputs{4, 6, 8};
QVector<int> v0;
QVector<int> v1;
auto p0 = QtPromise::reduce(inputs, [&](int acc, int cur, int idx) {
v0 << acc << cur << idx;
if (cur == 6) {
throw QString("foo");
}
return acc + cur + idx;
});
auto p1 = QtPromise::reduce(inputs, [&](int acc, int cur, int idx) {
v1 << acc << cur << idx;
if (cur == 6) {
throw QString("bar");
}
return acc + cur + idx;
}, 2);
Q_STATIC_ASSERT((std::is_same<decltype(p0), QPromise<int>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1), QPromise<int>>::value));
QCOMPARE(p0.isPending(), true);
QCOMPARE(p1.isPending(), true);
QCOMPARE(waitForError(p0, QString()), QString("foo"));
QCOMPARE(waitForError(p1, QString()), QString("bar"));
QCOMPARE(v0, QVector<int>({4, 6, 1}));
QCOMPARE(v1, QVector<int>({2, 4, 0, 6, 6, 1}));
}
void tst_helpers_reduce::sequenceTypes()
{
SequenceTester<QLinkedList<QPromise<int>>>::exec();
SequenceTester<QList<QPromise<int>>>::exec();
SequenceTester<QVector<QPromise<int>>>::exec();
SequenceTester<std::list<QPromise<int>>>::exec();
SequenceTester<std::vector<QPromise<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

@ -1,4 +1,3 @@
// Tests
#include "../../shared/utils.h"
// QtPromise
@ -12,7 +11,7 @@
using namespace QtPromise;
class tst_qpromise_reject : public QObject
class tst_helpers_reject : public QObject
{
Q_OBJECT
@ -22,10 +21,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 +33,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 +53,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

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

View File

@ -0,0 +1,268 @@
#include "../../shared/data.h"
#include "../../shared/utils.h"
// QtPromise
#include <QtPromise>
// Qt
#include <QtConcurrent>
#include <QtTest>
// STL
#include <memory>
using namespace QtPromise;
class tst_helpers_resolve : public QObject
{
Q_OBJECT
private Q_SLOTS:
void value();
void noValue();
void moveRValue();
void copyLValue();
void qtSharedPtr();
void stdSharedPtr();
void typedPromise();
void voidPromise();
void typedFuture();
void voidFuture();
};
QTEST_MAIN(tst_helpers_resolve)
#include "tst_resolve.moc"
void tst_helpers_resolve::value()
{
int v0 = 42;
const int v1 = 42;
auto p0 = QtPromise::resolve(42);
auto p1 = QtPromise::resolve(v0);
auto p2 = QtPromise::resolve(v1);
Q_STATIC_ASSERT((std::is_same<decltype(p0), QPromise<int>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1), QPromise<int>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p2), QPromise<int>>::value));
for (const auto& p : {p0, p1, p2}) {
QCOMPARE(p.isFulfilled(), true);
}
for (const auto& p : {p0, p1, p2}) {
QCOMPARE(waitForValue(p, -1), 42);
}
}
void tst_helpers_resolve::noValue()
{
auto p = QtPromise::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::moveRValue()
{
Data::logs().reset();
{
auto p = QtPromise::resolve(Data(42)).wait();
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<Data>>::value));
}
QCOMPARE(Data::logs().ctor, 1);
QCOMPARE(Data::logs().copy, 0);
QCOMPARE(Data::logs().move, 1);
QCOMPARE(Data::logs().refs, 0);
}
void tst_helpers_resolve::copyLValue()
{
Data::logs().reset();
{
Data value(42);
auto p = QtPromise::resolve(value).wait();
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<Data>>::value));
}
QCOMPARE(Data::logs().ctor, 1);
QCOMPARE(Data::logs().copy, 1);
QCOMPARE(Data::logs().move, 0);
QCOMPARE(Data::logs().refs, 0);
}
// https://github.com/simonbrunel/qtpromise/issues/6
void tst_helpers_resolve::qtSharedPtr()
{
Data::logs().reset();
QWeakPointer<Data> wptr;
{
QSharedPointer<Data> sptr0(new Data(42));
const QSharedPointer<Data> sptr1 = sptr0;
auto p0 = QtPromise::resolve(QSharedPointer<Data>(new Data(42)));
auto p1 = QtPromise::resolve(sptr0);
auto p2 = QtPromise::resolve(sptr1);
Q_STATIC_ASSERT((std::is_same<decltype(p0), QPromise<QSharedPointer<Data>>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1), QPromise<QSharedPointer<Data>>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p2), QPromise<QSharedPointer<Data>>>::value));
QCOMPARE(waitForValue(p1, QSharedPointer<Data>()), sptr0);
QCOMPARE(waitForValue(p2, QSharedPointer<Data>()), sptr1);
wptr = sptr0;
QCOMPARE(wptr.isNull(), false);
QCOMPARE(Data::logs().refs, 2);
}
QCOMPARE(wptr.isNull(), true);
QCOMPARE(Data::logs().ctor, 2);
QCOMPARE(Data::logs().copy, 0);
QCOMPARE(Data::logs().move, 0);
QCOMPARE(Data::logs().refs, 0);
}
// https://github.com/simonbrunel/qtpromise/issues/6
void tst_helpers_resolve::stdSharedPtr()
{
Data::logs().reset();
std::weak_ptr<Data> wptr;
{
std::shared_ptr<Data> sptr0(new Data(42));
const std::shared_ptr<Data> sptr1 = sptr0;
auto p0 = QtPromise::resolve(std::shared_ptr<Data>(new Data(42)));
auto p1 = QtPromise::resolve(sptr0);
auto p2 = QtPromise::resolve(sptr1);
Q_STATIC_ASSERT((std::is_same<decltype(p0), QPromise<std::shared_ptr<Data>>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1), QPromise<std::shared_ptr<Data>>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p2), QPromise<std::shared_ptr<Data>>>::value));
QCOMPARE(waitForValue(p1, std::shared_ptr<Data>()), sptr0);
QCOMPARE(waitForValue(p2, std::shared_ptr<Data>()), sptr1);
wptr = sptr0;
QCOMPARE(wptr.use_count(), 4l);
QCOMPARE(Data::logs().refs, 2);
}
QCOMPARE(wptr.use_count(), 0l);
QCOMPARE(Data::logs().ctor, 2);
QCOMPARE(Data::logs().copy, 0);
QCOMPARE(Data::logs().move, 0);
QCOMPARE(Data::logs().refs, 0);
}
void tst_helpers_resolve::typedPromise()
{
auto resolver = [](const QPromiseResolve<int>& resolve) {
QtPromisePrivate::qtpromise_defer([=](){
resolve(42);
});
};
QPromise<int> v0(resolver);
const QPromise<int> v1 = v0;
auto p0 = QtPromise::resolve(QPromise<int>(resolver));
auto p1 = QtPromise::resolve(v0);
auto p2 = QtPromise::resolve(v1);
Q_STATIC_ASSERT((std::is_same<decltype(p0), QPromise<int>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1), QPromise<int>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p2), QPromise<int>>::value));
for (const auto& promise : {p0, p1, p2}) {
QCOMPARE(promise.isPending(), true);
}
for (const auto& promise : {p0, p1, p2}) {
QCOMPARE(waitForValue(promise, -1), 42);
}
}
void tst_helpers_resolve::voidPromise()
{
auto resolver = [](const QPromiseResolve<void>& resolve) {
QtPromisePrivate::qtpromise_defer([=](){
resolve();
});
};
QPromise<void> v0(resolver);
const QPromise<void> v1 = v0;
auto p0 = QtPromise::resolve(QPromise<void>(resolver));
auto p1 = QtPromise::resolve(v0);
auto p2 = QtPromise::resolve(v1);
Q_STATIC_ASSERT((std::is_same<decltype(p0), QPromise<void>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1), QPromise<void>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p2), QPromise<void>>::value));
for (const auto& promise : {p0, p1, p2}) {
QCOMPARE(promise.isPending(), true);
}
for (const auto& promise : {p0, p1, p2}) {
QCOMPARE(waitForValue(promise, -1, 42), 42);
}
}
void tst_helpers_resolve::typedFuture()
{
auto fn = [](){ return 42; };
QFuture<int> v0 = QtConcurrent::run(fn);
const QFuture<int> v1 = v0;
auto p0 = QtPromise::resolve(QtConcurrent::run(fn));
auto p1 = QtPromise::resolve(v0);
auto p2 = QtPromise::resolve(v1);
Q_STATIC_ASSERT((std::is_same<decltype(p0), QPromise<int>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1), QPromise<int>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p2), QPromise<int>>::value));
for (const auto& promise : {p0, p1, p2}) {
QCOMPARE(promise.isPending(), true);
}
for (const auto& promise : {p0, p1, p2}) {
QCOMPARE(waitForValue(promise, -1), 42);
}
}
void tst_helpers_resolve::voidFuture()
{
auto fn = [](){ };
QFuture<void> v0 = QtConcurrent::run(fn);
const QFuture<void> v1 = v0;
auto p0 = QtPromise::resolve(QtConcurrent::run(fn));
auto p1 = QtPromise::resolve(v0);
auto p2 = QtPromise::resolve(v1);
Q_STATIC_ASSERT((std::is_same<decltype(p0), QPromise<void>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1), QPromise<void>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p2), QPromise<void>>::value));
for (const auto& promise : {p0, p1, p2}) {
QCOMPARE(promise.isPending(), true);
}
for (const auto& promise : {p0, p1, p2}) {
QCOMPARE(waitForValue(promise, -1, 42), 42);
}
}

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

Some files were not shown because too many files have changed in this diff Show More