300 lines
9.8 KiB
C++
300 lines
9.8 KiB
C++
|
#include "Promise.h"
|
||
|
#include <boost/test/unit_test.hpp>
|
||
|
#include <sstream>
|
||
|
#include <variant>
|
||
|
#include <vector>
|
||
|
|
||
|
template <typename T>
|
||
|
static inline T waitForValue(const Promise<T> &promise, const T &initial) {
|
||
|
T value(initial);
|
||
|
promise.then([&](const T &res) { value = res; }).wait();
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
static inline T waitForValue(const Promise<void> &promise, const T &initial, const T &expected) {
|
||
|
T value(initial);
|
||
|
promise.then([&]() { value = expected; }).wait();
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
BOOST_AUTO_TEST_CASE(Then) {
|
||
|
std::vector<std::variant<int, std::string>> values;
|
||
|
auto input = Promise<int>::resolve(42);
|
||
|
auto output = input.then([&](int res) {
|
||
|
values.push_back(res);
|
||
|
return std::to_string(res + 1);
|
||
|
});
|
||
|
output.then([&](const std::string &res) { values.push_back(res); }).then([&]() { values.push_back(44); }).wait();
|
||
|
|
||
|
BOOST_CHECK_EQUAL(values.size(), 3);
|
||
|
BOOST_CHECK_EQUAL(std::get<int>(values[0]), 42);
|
||
|
BOOST_CHECK_EQUAL(std::get<std::string>(values[1]), "43");
|
||
|
BOOST_CHECK_EQUAL(std::get<int>(values[2]), 44);
|
||
|
|
||
|
BOOST_CHECK_EQUAL(input.isFulfilled(), true);
|
||
|
BOOST_CHECK_EQUAL(output.isFulfilled(), true);
|
||
|
|
||
|
// auto p= new int;
|
||
|
}
|
||
|
|
||
|
BOOST_AUTO_TEST_CASE(ThenResolveAsync) {
|
||
|
std::thread thread;
|
||
|
auto promise = Promise<int>::resolve(42).then([&thread](int res) {
|
||
|
return Promise<std::string>{[&thread, res](const PromiseResolve<std::string> &resolve) {
|
||
|
thread = std::thread([resolve, res]() {
|
||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||
|
std::ostringstream oss;
|
||
|
oss << "foo" << res;
|
||
|
resolve(oss.str());
|
||
|
});
|
||
|
}};
|
||
|
});
|
||
|
|
||
|
std::string value;
|
||
|
promise.then([&](const std::string &res) { value = res; }).wait();
|
||
|
|
||
|
static_assert((std::is_same_v<decltype(promise), Promise<std::string>>));
|
||
|
BOOST_CHECK_EQUAL(value, "foo42");
|
||
|
BOOST_CHECK_EQUAL(promise.isFulfilled(), true);
|
||
|
|
||
|
if (thread.joinable()) {
|
||
|
thread.join();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BOOST_AUTO_TEST_CASE(ThenRejectSync) {
|
||
|
auto input = Promise<int>::resolve(42);
|
||
|
auto output = input.then([](int res) {
|
||
|
std::ostringstream oss;
|
||
|
oss << "foo" << res;
|
||
|
throw oss.str();
|
||
|
return 42;
|
||
|
});
|
||
|
|
||
|
std::string error;
|
||
|
output.then([&](int res) { error += "bar" + std::to_string(res); }).fail([&](const std::string &err) { error += err; }).wait();
|
||
|
|
||
|
BOOST_CHECK_EQUAL(error, "foo42");
|
||
|
BOOST_CHECK_EQUAL(input.isFulfilled(), true);
|
||
|
BOOST_CHECK_EQUAL(output.isRejected(), true);
|
||
|
}
|
||
|
|
||
|
BOOST_AUTO_TEST_CASE(ThenRejectAsync) {
|
||
|
std::thread thread;
|
||
|
auto p = Promise<int>::resolve(42).then([&thread](int res) {
|
||
|
return Promise<void>{[&thread, res](const PromiseResolve<void> &, const PromiseReject<void> &reject) {
|
||
|
thread = std::thread([reject, res]() {
|
||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||
|
std::ostringstream oss;
|
||
|
oss << "foo" << res;
|
||
|
reject(oss.str());
|
||
|
});
|
||
|
}};
|
||
|
});
|
||
|
|
||
|
static_assert((std::is_same_v<decltype(p), Promise<void>>));
|
||
|
|
||
|
std::string error;
|
||
|
p.fail([&](const std::string &err) { error = err; }).wait();
|
||
|
|
||
|
BOOST_CHECK_EQUAL(error, "foo42");
|
||
|
BOOST_CHECK_EQUAL(p.isRejected(), true);
|
||
|
|
||
|
if (thread.joinable()) {
|
||
|
thread.join();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BOOST_AUTO_TEST_CASE(ThenSkipResult) {
|
||
|
auto p = Promise<int>::resolve(42);
|
||
|
|
||
|
int value = -1;
|
||
|
p.then([&]() { value = 43; }).wait();
|
||
|
|
||
|
static_assert((std::is_same<decltype(p), Promise<int>>::value));
|
||
|
BOOST_CHECK_EQUAL(value, 43);
|
||
|
}
|
||
|
|
||
|
BOOST_AUTO_TEST_CASE(ThenNullHandler) {
|
||
|
{ // resolved
|
||
|
auto p = Promise<int>::resolve(42).then(nullptr);
|
||
|
|
||
|
int value;
|
||
|
p.then([&](const int &res) { value = res; }).wait();
|
||
|
|
||
|
BOOST_CHECK_EQUAL(value, 42);
|
||
|
BOOST_CHECK_EQUAL(p.isFulfilled(), true);
|
||
|
}
|
||
|
{ // rejected
|
||
|
auto p = Promise<int>::reject(std::string{"foo"}).then(nullptr);
|
||
|
|
||
|
std::string error;
|
||
|
p.fail([&](const std::string &err) {
|
||
|
error = err;
|
||
|
return 0;
|
||
|
}).wait();
|
||
|
BOOST_CHECK_EQUAL(error, "foo");
|
||
|
BOOST_CHECK_EQUAL(p.isRejected(), true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const float kRes = 0.42f;
|
||
|
const float kFail = -1.f;
|
||
|
|
||
|
float fnNoArg() {
|
||
|
return kRes;
|
||
|
}
|
||
|
|
||
|
float fnArgByVal(float v) {
|
||
|
return v;
|
||
|
}
|
||
|
float fnArgByRef(const float &v) {
|
||
|
return v;
|
||
|
}
|
||
|
|
||
|
class Klass {
|
||
|
public: // STATICS
|
||
|
static float kFnNoArg() {
|
||
|
return kRes;
|
||
|
}
|
||
|
static float kFnArgByVal(float v) {
|
||
|
return v;
|
||
|
}
|
||
|
static float kFnArgByRef(const float &v) {
|
||
|
return v;
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
Klass(float v) : m_v{v} {
|
||
|
}
|
||
|
|
||
|
float fnNoArg() const {
|
||
|
return m_v;
|
||
|
}
|
||
|
float fnArgByVal(float v) const {
|
||
|
return v + m_v;
|
||
|
}
|
||
|
float fnArgByRef(const float &v) const {
|
||
|
return v + m_v;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
const float m_v;
|
||
|
};
|
||
|
|
||
|
BOOST_AUTO_TEST_CASE(ThenFunctionPtrHandlers) {
|
||
|
{ // Global functions.
|
||
|
auto p0 = Promise<void>::resolve().then(&fnNoArg);
|
||
|
auto p1 = Promise<float>::resolve(kRes).then(&fnArgByVal);
|
||
|
auto p2 = Promise<float>::resolve(kRes).then(&fnArgByRef);
|
||
|
|
||
|
BOOST_CHECK_EQUAL(waitForValue(p0, kFail), kRes);
|
||
|
BOOST_CHECK_EQUAL(waitForValue(p1, kFail), kRes);
|
||
|
BOOST_CHECK_EQUAL(waitForValue(p2, kFail), kRes);
|
||
|
}
|
||
|
{ // Static member functions.
|
||
|
auto p0 = Promise<void>::resolve().then(&Klass::kFnNoArg);
|
||
|
auto p1 = Promise<float>::resolve(kRes).then(&Klass::kFnArgByVal);
|
||
|
auto p2 = Promise<float>::resolve(kRes).then(&Klass::kFnArgByRef);
|
||
|
|
||
|
BOOST_CHECK_EQUAL(waitForValue(p0, kFail), kRes);
|
||
|
BOOST_CHECK_EQUAL(waitForValue(p1, kFail), kRes);
|
||
|
BOOST_CHECK_EQUAL(waitForValue(p2, kFail), kRes);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BOOST_AUTO_TEST_CASE(ThenStdFunctionHandlers) {
|
||
|
{ // lvalue.
|
||
|
std::function<float()> stdFnNoArg = fnNoArg;
|
||
|
std::function<float(float)> stdFnArgByVal = fnArgByVal;
|
||
|
std::function<float(const float &)> stdFnArgByRef = fnArgByRef;
|
||
|
|
||
|
auto p0 = Promise<void>::resolve().then(stdFnNoArg);
|
||
|
auto p1 = Promise<float>::resolve(kRes).then(stdFnArgByVal);
|
||
|
auto p2 = Promise<float>::resolve(kRes).then(stdFnArgByRef);
|
||
|
|
||
|
BOOST_CHECK_EQUAL(waitForValue(p0, kFail), kRes);
|
||
|
BOOST_CHECK_EQUAL(waitForValue(p1, kFail), kRes);
|
||
|
BOOST_CHECK_EQUAL(waitForValue(p2, kFail), kRes);
|
||
|
}
|
||
|
{ // const lvalue.
|
||
|
const std::function<float()> stdFnNoArg = fnNoArg;
|
||
|
const std::function<float(float)> stdFnArgByVal = fnArgByVal;
|
||
|
const std::function<float(const float &)> stdFnArgByRef = fnArgByRef;
|
||
|
|
||
|
auto p0 = Promise<void>::resolve().then(stdFnNoArg);
|
||
|
auto p1 = Promise<float>::resolve(kRes).then(stdFnArgByVal);
|
||
|
auto p2 = Promise<float>::resolve(kRes).then(stdFnArgByRef);
|
||
|
|
||
|
BOOST_CHECK_EQUAL(waitForValue(p0, kFail), kRes);
|
||
|
BOOST_CHECK_EQUAL(waitForValue(p1, kFail), kRes);
|
||
|
BOOST_CHECK_EQUAL(waitForValue(p2, kFail), kRes);
|
||
|
}
|
||
|
{ // rvalue.
|
||
|
auto p0 = Promise<void>::resolve().then(std::function<float()>{fnNoArg});
|
||
|
auto p1 = Promise<float>::resolve(kRes).then(std::function<float(float)>{fnArgByVal});
|
||
|
auto p2 = Promise<float>::resolve(kRes).then(std::function<float(const float &)>{fnArgByRef});
|
||
|
|
||
|
BOOST_CHECK_EQUAL(waitForValue(p0, kFail), kRes);
|
||
|
BOOST_CHECK_EQUAL(waitForValue(p1, kFail), kRes);
|
||
|
BOOST_CHECK_EQUAL(waitForValue(p2, kFail), kRes);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BOOST_AUTO_TEST_CASE(ThenStdBindHandlers) {
|
||
|
using namespace std::placeholders;
|
||
|
|
||
|
const float val{42.f};
|
||
|
const Klass obj{val};
|
||
|
|
||
|
const std::function<float()> bindNoArg = std::bind(&Klass::fnNoArg, &obj);
|
||
|
const std::function<float(float)> bindArgByVal = std::bind(&Klass::fnArgByVal, &obj, _1);
|
||
|
const std::function<float(const float &)> bindArgByRef = std::bind(&Klass::fnArgByRef, &obj, _1);
|
||
|
|
||
|
auto p0 = Promise<void>::resolve().then(bindNoArg);
|
||
|
auto p1 = Promise<float>::resolve(kRes).then(bindArgByVal);
|
||
|
auto p2 = Promise<float>::resolve(kRes).then(bindArgByRef);
|
||
|
|
||
|
BOOST_CHECK_EQUAL(waitForValue(p0, kFail), val);
|
||
|
BOOST_CHECK_EQUAL(waitForValue(p1, kFail), val + kRes);
|
||
|
BOOST_CHECK_EQUAL(waitForValue(p2, kFail), val + kRes);
|
||
|
}
|
||
|
|
||
|
BOOST_AUTO_TEST_CASE(ThenLambdaHandlers) {
|
||
|
{ // lvalue.
|
||
|
auto lambdaNoArg = []() { return kRes; };
|
||
|
auto lambdaArgByVal = [](float v) { return v; };
|
||
|
auto lambdaArgByRef = [](const float &v) { return v; };
|
||
|
|
||
|
auto p0 = Promise<void>::resolve().then(lambdaNoArg);
|
||
|
auto p1 = Promise<float>::resolve(kRes).then(lambdaArgByVal);
|
||
|
auto p2 = Promise<float>::resolve(kRes).then(lambdaArgByRef);
|
||
|
|
||
|
BOOST_CHECK_EQUAL(waitForValue(p0, kFail), kRes);
|
||
|
BOOST_CHECK_EQUAL(waitForValue(p1, kFail), kRes);
|
||
|
BOOST_CHECK_EQUAL(waitForValue(p2, kFail), kRes);
|
||
|
}
|
||
|
{ // const lvalue.
|
||
|
const auto lambdaNoArg = []() { return kRes; };
|
||
|
const auto lambdaArgByVal = [](float v) { return v; };
|
||
|
const auto lambdaArgByRef = [](const float &v) { return v; };
|
||
|
|
||
|
auto p0 = Promise<void>::resolve().then(lambdaNoArg);
|
||
|
auto p1 = Promise<float>::resolve(kRes).then(lambdaArgByVal);
|
||
|
auto p2 = Promise<float>::resolve(kRes).then(lambdaArgByRef);
|
||
|
|
||
|
BOOST_CHECK_EQUAL(waitForValue(p0, kFail), kRes);
|
||
|
BOOST_CHECK_EQUAL(waitForValue(p1, kFail), kRes);
|
||
|
BOOST_CHECK_EQUAL(waitForValue(p2, kFail), kRes);
|
||
|
}
|
||
|
{ // rvalue.
|
||
|
auto p0 = Promise<void>::resolve().then([]() { return kRes; });
|
||
|
auto p1 = Promise<float>::resolve(kRes).then([](float v) { return v; });
|
||
|
auto p2 = Promise<float>::resolve(kRes).then([](const float &v) { return v; });
|
||
|
|
||
|
BOOST_CHECK_EQUAL(waitForValue(p0, kFail), kRes);
|
||
|
BOOST_CHECK_EQUAL(waitForValue(p1, kFail), kRes);
|
||
|
BOOST_CHECK_EQUAL(waitForValue(p2, kFail), kRes);
|
||
|
}
|
||
|
}
|