#include "Promise.h" #include #include #include #include template static inline T waitForValue(const Promise &promise, const T &initial) { T value(initial); promise.then([&](const T &res) { value = res; }).wait(); return value; } template static inline T waitForValue(const Promise &promise, const T &initial, const T &expected) { T value(initial); promise.then([&]() { value = expected; }).wait(); return value; } BOOST_AUTO_TEST_CASE(Then) { std::vector> values; auto input = Promise::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(values[0]), 42); BOOST_CHECK_EQUAL(std::get(values[1]), "43"); BOOST_CHECK_EQUAL(std::get(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::resolve(42).then([&thread](int res) { return Promise{[&thread, res](const PromiseResolve &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>)); BOOST_CHECK_EQUAL(value, "foo42"); BOOST_CHECK_EQUAL(promise.isFulfilled(), true); if (thread.joinable()) { thread.join(); } } BOOST_AUTO_TEST_CASE(ThenRejectSync) { auto input = Promise::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::resolve(42).then([&thread](int res) { return Promise{[&thread, res](const PromiseResolve &, const PromiseReject &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>)); 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::resolve(42); int value = -1; p.then([&]() { value = 43; }).wait(); static_assert((std::is_same>::value)); BOOST_CHECK_EQUAL(value, 43); } BOOST_AUTO_TEST_CASE(ThenNullHandler) { { // resolved auto p = Promise::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::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::resolve().then(&fnNoArg); auto p1 = Promise::resolve(kRes).then(&fnArgByVal); auto p2 = Promise::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::resolve().then(&Klass::kFnNoArg); auto p1 = Promise::resolve(kRes).then(&Klass::kFnArgByVal); auto p2 = Promise::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 stdFnNoArg = fnNoArg; std::function stdFnArgByVal = fnArgByVal; std::function stdFnArgByRef = fnArgByRef; auto p0 = Promise::resolve().then(stdFnNoArg); auto p1 = Promise::resolve(kRes).then(stdFnArgByVal); auto p2 = Promise::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 stdFnNoArg = fnNoArg; const std::function stdFnArgByVal = fnArgByVal; const std::function stdFnArgByRef = fnArgByRef; auto p0 = Promise::resolve().then(stdFnNoArg); auto p1 = Promise::resolve(kRes).then(stdFnArgByVal); auto p2 = Promise::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::resolve().then(std::function{fnNoArg}); auto p1 = Promise::resolve(kRes).then(std::function{fnArgByVal}); auto p2 = Promise::resolve(kRes).then(std::function{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 bindNoArg = std::bind(&Klass::fnNoArg, &obj); const std::function bindArgByVal = std::bind(&Klass::fnArgByVal, &obj, _1); const std::function bindArgByRef = std::bind(&Klass::fnArgByRef, &obj, _1); auto p0 = Promise::resolve().then(bindNoArg); auto p1 = Promise::resolve(kRes).then(bindArgByVal); auto p2 = Promise::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::resolve().then(lambdaNoArg); auto p1 = Promise::resolve(kRes).then(lambdaArgByVal); auto p2 = Promise::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::resolve().then(lambdaNoArg); auto p1 = Promise::resolve(kRes).then(lambdaArgByVal); auto p2 = Promise::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::resolve().then([]() { return kRes; }); auto p1 = Promise::resolve(kRes).then([](float v) { return v; }); auto p2 = Promise::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); } }