Kylin/UnitTest/Universal/PromiseTest.cpp
2024-09-15 16:15:26 +08:00

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