2022-06-26 18:10:20 +02:00

293 lines
9.1 KiB
C++

/*
* Copyright (c) Simon Brunel, https://github.com/simonbrunel
*
* This source code is licensed under the MIT license found in
* the LICENSE file in the root directory of this source tree.
*/
#include "../shared/data.h"
#include "../shared/utils.h"
#include <QtPromise>
#include <QtTest>
class tst_qpromise_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_qpromise_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::resolve(inputs).reduce([&](int acc, int cur, int idx) {
v0 << acc << cur << idx;
return acc + cur + idx;
});
auto p1 = QtPromise::resolve(inputs).reduce(
[&](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), QtPromise::QPromise<int>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1), QtPromise::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_qpromise_reduce::emptySequence()
{
bool called = false;
auto p = QtPromise::resolve(QVector<int>{})
.reduce(
[&](...) {
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), QtPromise::QPromise<int>>::value));
QCOMPARE(waitForValue(p, -1), 42);
QCOMPARE(called, false);
}
void tst_qpromise_reduce::regularValues()
{
QVector<int> inputs{4, 6, 8};
QVector<int> v0;
QVector<int> v1;
auto p0 = QtPromise::resolve(inputs).reduce([&](int acc, int cur, int idx) {
v0 << acc << cur << idx;
return acc + cur + idx;
});
auto p1 = QtPromise::resolve(inputs).reduce(
[&](int acc, int cur, int idx) {
v1 << acc << cur << idx;
return acc + cur + idx;
},
2);
Q_STATIC_ASSERT((std::is_same<decltype(p0), QtPromise::QPromise<int>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1), QtPromise::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_qpromise_reduce::promiseValues()
{
QVector<QtPromise::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::resolve(inputs).reduce([&](int acc, int cur, int idx) {
v0 << acc << cur << idx;
return acc + cur + idx;
});
auto p1 = QtPromise::resolve(inputs).reduce(
[&](int acc, int cur, int idx) {
v1 << acc << cur << idx;
return acc + cur + idx;
},
2);
Q_STATIC_ASSERT((std::is_same<decltype(p0), QtPromise::QPromise<int>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1), QtPromise::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_qpromise_reduce::convertResultType()
{
QVector<int> inputs{4, 6, 8};
auto p = QtPromise::resolve(inputs).reduce(
[&](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), QtPromise::QPromise<QString>>::value));
QCOMPARE(p.isPending(), true);
QCOMPARE(waitForValue(p, QString{}), QString{"foo:4:0:6:1:8:2"});
}
void tst_qpromise_reduce::delayedInitialValue()
{
QVector<int> values;
auto p = QtPromise::resolve(QVector<int>{4, 6, 8})
.reduce(
[&](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), QtPromise::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_qpromise_reduce::delayedFulfilled()
{
QVector<int> inputs{4, 6, 8};
QVector<int> v0;
QVector<int> v1;
auto p0 = QtPromise::resolve(inputs).reduce([&](int acc, int cur, int idx) {
v0 << acc << cur << idx;
return QtPromise::resolve(acc + cur + idx).delay(100);
});
auto p1 = QtPromise::resolve(inputs).reduce(
[&](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), QtPromise::QPromise<int>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1), QtPromise::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_qpromise_reduce::delayedRejected()
{
QVector<int> inputs{4, 6, 8};
QVector<int> v0;
QVector<int> v1;
auto p0 = QtPromise::resolve(inputs).reduce([&](int acc, int cur, int idx) {
v0 << acc << cur << idx;
if (cur == 6) {
return QtPromise::QPromise<int>::reject(QString{"foo"});
}
return QtPromise::resolve(acc + cur + idx);
});
auto p1 = QtPromise::resolve(inputs).reduce(
[&](int acc, int cur, int idx) {
v1 << acc << cur << idx;
if (cur == 6) {
return QtPromise::QPromise<int>::reject(QString{"bar"});
}
return QtPromise::resolve(acc + cur + idx);
},
2);
Q_STATIC_ASSERT((std::is_same<decltype(p0), QtPromise::QPromise<int>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1), QtPromise::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_qpromise_reduce::functorThrows()
{
QVector<int> inputs{4, 6, 8};
QVector<int> v0;
QVector<int> v1;
auto p0 = QtPromise::resolve(inputs).reduce([&](int acc, int cur, int idx) {
v0 << acc << cur << idx;
if (cur == 6) {
throw QString{"foo"};
}
return acc + cur + idx;
});
auto p1 = QtPromise::resolve(inputs).reduce(
[&](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), QtPromise::QPromise<int>>::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1), QtPromise::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_qpromise_reduce::sequenceTypes()
{
#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0))
// QLinkedList is deprecated since Qt 5.15.
SequenceTester<QLinkedList<QtPromise::QPromise<int>>>::exec();
#endif
SequenceTester<QList<QtPromise::QPromise<int>>>::exec();
SequenceTester<QVector<QtPromise::QPromise<int>>>::exec();
SequenceTester<std::list<QtPromise::QPromise<int>>>::exec();
SequenceTester<std::vector<QtPromise::QPromise<int>>>::exec();
}