blob: 7c18de7757c2f3eaeb90e85a350fc734dceb61b6 [file] [log] [blame]
/**
*Licensed to the Apache Software Foundation (ASF) under one
*or more contributor license agreements. See the NOTICE file
*distributed with this work for additional information
*regarding copyright ownership. The ASF licenses this file
*to you under the Apache License, Version 2.0 (the
*"License"); you may not use this file except in compliance
*with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*Unless required by applicable law or agreed to in writing,
*software distributed under the License is distributed on an
*"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
*specific language governing permissions and limitations
*under the License.
*/
#include <gtest/gtest.h>
#include <future>
#include <utility>
#include "celix/PromiseFactory.h"
#include "celix/DefaultExecutor.h"
#include "celix/DefaultScheduledExecutor.h"
/**
* A special executor which introduces some sleeps to ensure some entropy during testing
*/
class ExecutorWithRandomPrePostSleep : public celix::DefaultExecutor {
public:
ExecutorWithRandomPrePostSleep() : celix::DefaultExecutor{std::launch::async} {}
void execute( int priority, std::function<void()> task) override {
int roll1 = distribution(generator); //1-100
int roll2 = distribution(generator); //1-100
celix::DefaultExecutor::execute(priority, [roll1, roll2, task = std::move(task)] {
std::this_thread::sleep_for(std::chrono::milliseconds{roll1});
task();
std::this_thread::sleep_for(std::chrono::milliseconds{roll2});
});
}
private:
std::default_random_engine generator{};
std::uniform_int_distribution<int> distribution{1,100};
};
class PromiseTestSuite : public ::testing::Test {
public:
~PromiseTestSuite() noexcept override = default;
std::shared_ptr<celix::IExecutor> executor = std::make_shared<ExecutorWithRandomPrePostSleep>();
std::shared_ptr<celix::IScheduledExecutor> scheduledExecutor = std::make_shared<celix::DefaultScheduledExecutor>();
std::shared_ptr<celix::PromiseFactory> factory = std::make_shared<celix::PromiseFactory>(executor, scheduledExecutor);
};
struct MovableInt {
MovableInt(int _val) : val(_val) {}
operator int() const { return val; }
int val;
};
struct NonMovableInt {
NonMovableInt(int _val) : val(_val) {}
operator int() const { return val; }
NonMovableInt(NonMovableInt&&) = delete;
NonMovableInt(const NonMovableInt&) = default;
NonMovableInt& operator=(NonMovableInt&&) = delete;
NonMovableInt& operator=(const NonMovableInt&) = default;
int val;
};
struct NonTrivialType {
NonTrivialType(std::string _val) : val(std::move(_val)) {}
operator std::string() const { return val; }
NonTrivialType(NonTrivialType&&) = default;
NonTrivialType(const NonTrivialType&) = default;
NonTrivialType& operator=(NonTrivialType&&) = default;
NonTrivialType& operator=(const NonTrivialType&) = default;
NonTrivialType(const char *c) : val(c) {}
NonTrivialType& operator=(const char *c) {
val = c;
return *this;
}
bool operator==(const char *c) const {
return c == val;
}
std::string val;
};
bool operator==( const char *c, const NonTrivialType &ntt) {
return c == ntt.val;
}
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-result"
#endif
TEST_F(PromiseTestSuite, simplePromise) {
auto deferred = factory->deferred<NonTrivialType>();
std::thread t{[&deferred] () {
std::this_thread::sleep_for(std::chrono::milliseconds{50});
deferred.resolve("test");
}};
auto promise = deferred.getPromise();
EXPECT_EQ("test", promise.getValue()); //block until ready
EXPECT_TRUE(promise.isDone()); //got value, so promise is done
EXPECT_ANY_THROW(promise.getFailure()); //succeeded, so no exception available
EXPECT_EQ("test", promise.getValue()); //note multiple call are valid;
EXPECT_EQ("test", promise.moveOrGetValue()); //data is now moved
EXPECT_THROW(promise.getValue(), celix::PromiseInvocationException); //data is already moved -> exception
t.join();
}
TEST_F(PromiseTestSuite, failingPromise) {
auto deferred = factory->deferred<long>();
auto cpy = deferred;
std::thread t{[&deferred] () {
deferred.fail(std::logic_error{"failing"});
}};
auto promise = deferred.getPromise();
EXPECT_THROW(promise.getValue(), celix::PromiseInvocationException);
EXPECT_TRUE(promise.getFailure() != nullptr);
t.join();
}
TEST_F(PromiseTestSuite, failingPromiseWithExceptionPtr) {
auto deferred = factory->deferred<long>();
std::thread t{[&deferred]{
try {
std::string{}.at(1); // this generates an std::out_of_range
//or use std::make_exception_ptr
} catch (...) {
deferred.fail(std::current_exception());
}
}};
auto promise = deferred.getPromise();
EXPECT_THROW(promise.getValue(), celix::PromiseInvocationException);
EXPECT_TRUE(promise.getFailure() != nullptr);
t.join();
}
TEST_F(PromiseTestSuite, onSuccessHandling) {
auto deferred = factory->deferred<long>();
std::atomic<bool> called = false;
std::atomic<bool> resolveCalled = false;
auto p = deferred.getPromise()
.onSuccess([&](long value) {
EXPECT_EQ(42, value);
called = true;
})
.onResolve([&]() {
resolveCalled = true;
});
deferred.resolve(42);
factory->wait();
EXPECT_EQ(true, called);
EXPECT_EQ(true, resolveCalled);
}
static void test_throwing_function() {
throw celix::PromiseInvocationException{"PromiseInvocationException test"};
}
TEST_F(PromiseTestSuite, onFailureHandling) {
auto deferred = factory->deferred<long>();
std::atomic<bool> successCalled = false;
std::atomic<bool> failureCalled = false;
std::atomic<bool> resolveCalled = false;
auto p = deferred.getPromise()
.onSuccess([&](long /*value*/) {
successCalled = true;
})
.onFailure([&](const std::exception &e) {
failureCalled = true;
auto what = e.what();
ASSERT_STREQ(what, "PromiseInvocationException test");
})
.onResolve([&]() {
resolveCalled = true;
});
try {
test_throwing_function(); // this generates a celix::PromiseInvocationException
} catch (...) {
deferred.fail(std::current_exception());
}
factory->wait();
EXPECT_EQ(false, successCalled);
EXPECT_EQ(true, failureCalled);
EXPECT_EQ(true, resolveCalled);
}
TEST_F(PromiseTestSuite, onFailureHandlingLogicError) {
auto deferred = factory->deferred<long>();
std::atomic<bool> successCalled = false;
std::atomic<bool> failureCalled = false;
std::atomic<bool> resolveCalled = false;
auto p = deferred.getPromise()
.onSuccess([&](long /*value*/) {
successCalled = true;
})
.onFailure([&](const std::exception &e) {
failureCalled = true;
ASSERT_TRUE(0 == strcmp("Some Exception", e.what())) << std::string(e.what());
})
.onResolve([&]() {
resolveCalled = true;
});
deferred.fail(std::logic_error("Some Exception"));
factory->wait();
EXPECT_EQ(false, successCalled);
EXPECT_EQ(true, failureCalled);
EXPECT_EQ(true, resolveCalled);
}
TEST_F(PromiseTestSuite, onFailureHandlingPromiseIllegalStateException) {
auto deferred = factory->deferred<long>();
std::atomic<bool> successCalled = false;
std::atomic<bool> failureCalled = false;
std::atomic<bool> resolveCalled = false;
auto p = deferred.getPromise()
.onSuccess([&](long /*value*/) {
successCalled = true;
})
.onFailure([&](const std::exception &e) {
failureCalled = true;
ASSERT_TRUE(0 == strcmp("Illegal state", e.what())) << std::string(e.what());
})
.onResolve([&]() {
resolveCalled = true;
});
deferred.fail(celix::PromiseIllegalStateException());
factory->wait();
EXPECT_EQ(false, successCalled);
EXPECT_EQ(true, failureCalled);
EXPECT_EQ(true, resolveCalled);
}
TEST_F(PromiseTestSuite, onFailureHandlingPromiseInvocationException) {
auto deferred = factory->deferred<long>();
std::atomic<bool> successCalled = false;
std::atomic<bool> failureCalled = false;
std::atomic<bool> resolveCalled = false;
auto p = deferred.getPromise()
.onSuccess([&](long /*value*/) {
successCalled = true;
})
.onFailure([&](const std::exception &e) {
failureCalled = true;
ASSERT_TRUE(0 == strcmp("MyExceptionText", e.what())) << std::string(e.what());
})
.onResolve([&]() {
resolveCalled = true;
});
deferred.fail(celix::PromiseInvocationException("MyExceptionText"));
factory->wait();
EXPECT_EQ(false, successCalled);
EXPECT_EQ(true, failureCalled);
EXPECT_EQ(true, resolveCalled);
}
TEST_F(PromiseTestSuite, onFailureHandlingPromiseTimeoutException) {
auto deferred = factory->deferred<long>();
std::atomic<bool> successCalled = false;
std::atomic<bool> failureCalled = false;
std::atomic<bool> resolveCalled = false;
auto p = deferred.getPromise()
.onSuccess([&](long /*value*/) {
successCalled = true;
})
.onFailure([&](const std::exception &e) {
failureCalled = true;
ASSERT_TRUE(0 == strcmp("Timeout", e.what())) << std::string(e.what());
})
.onResolve([&]() {
resolveCalled = true;
});
deferred.fail(celix::PromiseTimeoutException());
factory->wait();
EXPECT_EQ(false, successCalled);
EXPECT_EQ(true, failureCalled);
EXPECT_EQ(true, resolveCalled);
}
TEST_F(PromiseTestSuite, onFailureHandlingRejectedExecutionException) {
auto deferred = factory->deferred<long>();
std::atomic<bool> successCalled = false;
std::atomic<bool> failureCalled = false;
std::atomic<bool> resolveCalled = false;
auto p = deferred.getPromise()
.onSuccess([&](long /*value*/) {
successCalled = true;
})
.onFailure([&](const std::exception &e) {
failureCalled = true;
ASSERT_TRUE(0 == strcmp("Cannot accept task for execution", e.what())) << std::string(e.what());
})
.onResolve([&]() {
resolveCalled = true;
});
deferred.fail(celix::RejectedExecutionException());
factory->wait();
EXPECT_EQ(false, successCalled);
EXPECT_EQ(true, failureCalled);
EXPECT_EQ(true, resolveCalled);
}
TEST_F(PromiseTestSuite, resolveSuccessWith) {
auto deferred1 = factory->deferred<long>();
std::atomic<bool> called = false;
deferred1.getPromise()
.onSuccess([&](long value) {
EXPECT_EQ(42, value);
called = true;
});
auto deferred2 = factory->deferred<long>();
deferred1.resolveWith(deferred2.getPromise());
deferred2.resolve(42);
factory->wait();
EXPECT_EQ(true, called);
}
TEST_F(PromiseTestSuite, resolveFailureWith) {
auto deferred1 = factory->deferred<long>();
auto deferred2 = factory->deferred<long>();
std::atomic<bool> failureCalled = false;
deferred2.getPromise()
.onFailure([&](const std::exception &e) {
std::cout << "got error: " << e.what() << std::endl;
failureCalled = true;
});
//currently deferred1 will be resolved in thread, and onSuccess is trigger on the promise of deferred2
//now resolving deferred2 with the promise of deferred1
deferred2.resolveWith(deferred1.getPromise());
auto p = deferred2.getPromise();
try {
std::string().at(1); // this generates an std::out_of_range
//or use std::make_exception_ptr
} catch (...) {
deferred1.fail(std::current_exception());
}
factory->wait();
EXPECT_EQ(true, failureCalled);
}
TEST_F(PromiseTestSuite, returnPromiseOnSuccessfulResolveWith) {
auto deferred1 = factory->deferred<long>();
auto deferred2 = factory->deferred<long>();
celix::Promise<void> promise = deferred1.resolveWith(deferred2.getPromise());
EXPECT_FALSE(promise.isDone());
deferred2.resolve(42);
factory->wait();
EXPECT_TRUE(promise.isDone());
EXPECT_TRUE(promise.isSuccessfullyResolved());
}
TEST_F(PromiseTestSuite, returnPromiseOnInvalidResolveWith) {
auto deferred1 = factory->deferred<long>();
auto deferred2 = factory->deferred<long>();
celix::Promise<void> promise = deferred1.resolveWith(deferred2.getPromise());
EXPECT_FALSE(promise.isDone());
deferred1.resolve(42); //Rule is deferred1 is resolved, before the resolveWith the returned promise should fail
deferred2.resolve(42);
factory->wait();
EXPECT_TRUE(promise.isDone());
EXPECT_FALSE(promise.isSuccessfullyResolved());
EXPECT_THROW(std::rethrow_exception(promise.getFailure()), celix::PromiseIllegalStateException);
}
//gh-333 fix timeout test on macos
#ifndef __APPLE__
TEST_F(PromiseTestSuite, resolveWithTimeout) {
auto promise1 = factory->deferredTask<long>([](auto d) {
std::this_thread::sleep_for(std::chrono::milliseconds{50});
try {
d.resolve(42);
} catch(...) {
//note resolve with throws an exception if promise is already resolved
}
});
std::atomic<bool> firstSuccessCalled = false;
std::atomic<bool> secondSuccessCalled = false;
std::atomic<bool> secondFailedCalled = false;
promise1
.onSuccess([&](long value) {
EXPECT_EQ(42, value);
firstSuccessCalled = true;
})
.timeout(std::chrono::milliseconds{5})
.onSuccess([&](long value) {
EXPECT_EQ(42, value);
secondSuccessCalled = true;
})
.onFailure([&](const std::exception&) {
secondFailedCalled = true;
});
promise1.wait();
factory->wait();
EXPECT_EQ(true, firstSuccessCalled);
EXPECT_EQ(false, secondSuccessCalled);
EXPECT_EQ(true, secondFailedCalled);
firstSuccessCalled = false;
secondSuccessCalled = false;
secondFailedCalled = false;
auto promise2 = factory->deferredTask<long>([](auto d) {
d.resolve(42);
});
promise2
.onSuccess([&](long value) {
EXPECT_EQ(42, value);
firstSuccessCalled = true;
})
.timeout(std::chrono::milliseconds{200}) /*NOTE: more than the possible delay introduced by the executor*/
.onSuccess([&](long value) {
EXPECT_EQ(42, value);
secondSuccessCalled = true;
})
.onFailure([&](const std::exception&) {
secondFailedCalled = true;
});
promise2.wait();
factory->wait();
EXPECT_EQ(true, firstSuccessCalled);
EXPECT_EQ(true, secondSuccessCalled);
EXPECT_EQ(false, secondFailedCalled);
}
#endif
TEST_F(PromiseTestSuite, resolveWithSetTimeout) {
auto promise = factory->deferred<int>().getPromise().setTimeout(std::chrono::milliseconds{5});
factory->wait();
EXPECT_TRUE(promise.isDone());
EXPECT_FALSE(promise.isSuccessfullyResolved());
}
TEST_F(PromiseTestSuite, resolveWithDelay) {
auto deferred1 = factory->deferred<long>();
std::atomic<bool> successCalled = false;
auto t1 = std::chrono::steady_clock::now();
std::atomic<std::chrono::steady_clock::time_point> t2{t1};
auto p = deferred1.getPromise()
.delay(std::chrono::milliseconds{50})
.onSuccess([&](long value) {
EXPECT_EQ(42, value);
t2 = std::chrono::steady_clock::now();
successCalled = true;
});
deferred1.resolve(42);
p.wait();
factory->wait();
EXPECT_EQ(true, successCalled.load());
auto durationInMs = std::chrono::duration_cast<std::chrono::milliseconds>(t2.load() - t1);
EXPECT_GE(durationInMs, std::chrono::milliseconds{50});
}
TEST_F(PromiseTestSuite, resolveWithRecover) {
auto deferred1 = factory->deferred<long>();
std::atomic<bool> successCalled = false;
deferred1.getPromise()
.recover([]{ return 42; })
.onSuccess([&](long v) {
EXPECT_EQ(42, v);
successCalled = true;
});
try {
throw std::logic_error("failure");
} catch (...) {
deferred1.fail(std::current_exception());
}
factory->wait();
EXPECT_EQ(true, successCalled);
}
TEST_F(PromiseTestSuite, chainAndMapResult) {
auto deferred1 = factory->deferred<long>();
std::thread t{[&deferred1]{
deferred1.resolve(42);
}};
int half = deferred1.getPromise()
.map<int>([](long v) {
return (int)v/2;
}).getValue();
t.join();
EXPECT_EQ(21, half);
}
TEST_F(PromiseTestSuite, chainWithThenAccept) {
auto deferred1 = factory->deferred<long>();
std::atomic<bool> called = false;
deferred1.getPromise()
.thenAccept([&](long v){
EXPECT_EQ(42, v);
called = true;
});
deferred1.resolve(42);
factory->wait();
EXPECT_TRUE(called);
}
TEST_F(PromiseTestSuite, promiseWithFallbackTo) {
celix::Deferred<long> deferred1 = factory->deferred<long>();
try {
throw std::logic_error("failure");
} catch (...) {
deferred1.fail(std::current_exception());
}
auto deferred2 = factory->deferred<long>();
deferred2.resolve(42);
long val = deferred1.getPromise().fallbackTo(deferred2.getPromise()).getValue();
EXPECT_EQ(42, val);
}
TEST_F(PromiseTestSuite, promiseWithPredicate) {
auto deferred1 = factory->deferred<long>();
std::thread t1{[&deferred1]{
deferred1.resolve(42);
}};
EXPECT_ANY_THROW(deferred1.getPromise().filter([](long v) {return v == 0; }).getValue());
EXPECT_EQ(42, deferred1.getPromise().filter([](long v) {return v == 42; }).getValue());
t1.join();
}
TEST_F(PromiseTestSuite, outOfScopeUnresolvedPromises) {
bool called = false;
{
auto deferred1 = factory->deferred<long>();
deferred1.getPromise().onResolve([&]{
called = true;
});
//promise and deferred out of scope
}
EXPECT_FALSE(called);
}
TEST_F(PromiseTestSuite, chainPromises) {
auto success = [&](celix::Promise<long> p) -> celix::Promise<long> {
auto result = factory->deferred<long>();
result.resolve(p.getValue() + p.getValue());
return result.getPromise();
};
auto initial = factory->deferred<long>();
initial.resolve(42);
long result = initial.getPromise().then<long>(success).then<long>(success).getValue();
EXPECT_EQ(168, result);
}
TEST_F(PromiseTestSuite, chainFailedPromises) {
bool called = false;
auto success = [](celix::Promise<long> p) -> celix::Promise<long> {
//nop
return p;
};
auto failed = [&called](const celix::Promise<long>& /*p*/) -> void {
called = true;
};
auto deferred = factory->deferred<long>();
deferred.fail(std::logic_error{"fail"});
deferred.getPromise().then<long>(success, failed);
factory->wait();
EXPECT_TRUE(called);
}
TEST_F(PromiseTestSuite, failedResolvedWithPromiseFactory) {
auto p1 = factory->failed<long>(std::logic_error{"test"});
EXPECT_TRUE(p1.isDone());
EXPECT_NE(nullptr, p1.getFailure());
auto p2 = factory->resolved(42);
EXPECT_TRUE(p2.isDone());
EXPECT_EQ(42, p2.getValue());
}
TEST_F(PromiseTestSuite, movableStruct) {
auto deferred = factory->deferred<MovableInt>();
std::thread t{[&deferred] () {
std::this_thread::sleep_for(std::chrono::milliseconds{50});
deferred.resolve(42);
}};
auto promise = deferred.getPromise();
EXPECT_EQ(42, promise.getValue());
EXPECT_EQ(42, *promise);
t.join();
}
TEST_F(PromiseTestSuite, movableStructTemporary) {
auto deferred = factory->deferred<MovableInt>();
std::thread t{[&deferred] () {
std::this_thread::sleep_for(std::chrono::milliseconds{50});
deferred.resolve(42);
}};
EXPECT_EQ(42, deferred.getPromise().getValue());
t.join();
}
TEST_F(PromiseTestSuite, nonMovableStruct) {
auto deferred = factory->deferred<NonMovableInt>();
std::thread t{[&deferred] () {
std::this_thread::sleep_for(std::chrono::milliseconds{50});
deferred.resolve(42);
}};
auto promise = deferred.getPromise();
EXPECT_EQ(42, promise.getValue());
EXPECT_EQ(42, *promise);
t.join();
}
TEST_F(PromiseTestSuite, nonMovableStructTemporary) {
auto deferred = factory->deferred<NonMovableInt>();
std::thread t{[&deferred] () {
std::this_thread::sleep_for(std::chrono::milliseconds{50});
deferred.resolve(42);
}};
EXPECT_EQ(42, deferred.getPromise().getValue());
t.join();
}
TEST_F(PromiseTestSuite, deferredTaskCall) {
auto promise = factory->deferredTask<long>([](auto deferred) {
deferred.resolve(42);
});
EXPECT_EQ(42, promise.getValue());
}
TEST_F(PromiseTestSuite, getExecutorFromFactory) {
auto exec = factory->getExecutor();
EXPECT_EQ(executor.get(), exec.get());
}
TEST_F(PromiseTestSuite, testRobustFailAndResolve) {
std::atomic<int> failCount{};
std::atomic<int> successCount{};
auto failCb = [&failCount](const std::exception& /*e*/) {
failCount++;
};
auto successCb = [&successCount](int val) {
EXPECT_EQ(42, val);
successCount++;
};
auto def = factory->deferred<int>();
def.getPromise().onFailure(failCb);
//Rule a second fail should not lead to an exception, to ensure a more robust usage.
//But also should only lead to a single resolve chain.
def.fail(std::logic_error{"error1"});
def.fail(std::logic_error{"error2"});
factory->wait();
EXPECT_EQ(failCount.load(), 1);
def = factory->deferred<int>();
def.getPromise().onSuccess(successCb);
//Rule a second resolve should not lead to an exception, to ensure a more robust usage.
//But also should only lead to a single resolve chain.
def.resolve(42);
def.resolve(43);
factory->wait();
EXPECT_EQ(successCount.load(), 1);
failCount = 0;
successCount = 0;
def = factory->deferred<int>();
def.getPromise().onSuccess(successCb).onFailure(failCb);
//Rule a resolve after fail should not lead to an exception, to ensure a more robust usage.
//But also should only lead to a single resolve chain.
def.fail(std::logic_error("error3"));
def.resolve(43);
factory->wait();
EXPECT_EQ(failCount.load(), 1);
EXPECT_EQ(successCount.load(), 0);
failCount = 0;
successCount = 0;
def = factory->deferred<int>();
def.getPromise().onSuccess(successCb).onFailure(failCb);
//Rule a fail after resolve should not lead to an exception, to ensure a more robust usage.
//But also should only lead to a single resolve chain.
def.resolve(42);
def.fail(std::logic_error("error3"));
factory->wait();
EXPECT_EQ(failCount.load(), 0);
EXPECT_EQ(successCount.load(), 1);
}
TEST_F(PromiseTestSuite, testTryFailAndResolve) {
std::atomic<int> failCount{};
std::atomic<int> successCount{};
auto failCb = [&failCount](const std::exception& /*e*/) {
failCount++;
};
auto successCb = [&successCount](int val) {
EXPECT_EQ(42, val);
successCount++;
};
const int val = 42;
//first resolve with &&, then try rest
auto def = factory->deferred<int>();
def.getPromise().onSuccess(successCb).onFailure(failCb);
EXPECT_TRUE(def.tryResolve(42));
EXPECT_FALSE(def.tryResolve(43));
EXPECT_FALSE(def.tryFail(std::logic_error{"error"}));
EXPECT_FALSE(def.tryFail(std::make_exception_ptr(std::logic_error{"error"})));
factory->wait();
EXPECT_EQ(failCount.load(), 0);
EXPECT_EQ(successCount.load(), 1);
//first resolve with const int&, then try rest
failCount = 0;
successCount = 0;
def = factory->deferred<int>();
def.getPromise().onSuccess(successCb).onFailure(failCb);
EXPECT_TRUE(def.tryResolve(val));
EXPECT_FALSE(def.tryResolve(43));
EXPECT_FALSE(def.tryResolve(val));
EXPECT_FALSE(def.tryFail(std::logic_error{"error"}));
EXPECT_FALSE(def.tryFail(std::make_exception_ptr(std::logic_error{"error"})));
factory->wait();
EXPECT_EQ(failCount.load(), 0);
EXPECT_EQ(successCount.load(), 1);
//first fail with exp ref, then try rest
failCount = 0;
successCount = 0;
def = factory->deferred<int>();
def.getPromise().onSuccess(successCb).onFailure(failCb);
EXPECT_TRUE(def.tryFail(std::logic_error{"error"}));
EXPECT_FALSE(def.tryResolve(43));
EXPECT_FALSE(def.tryResolve(val));
EXPECT_FALSE(def.tryFail(std::logic_error{"error"}));
EXPECT_FALSE(def.tryFail(std::make_exception_ptr(std::logic_error{"error"})));
factory->wait();
EXPECT_EQ(failCount.load(), 1);
EXPECT_EQ(successCount.load(), 0);
//first fail with exp ptr, then try rest
failCount = 0;
successCount = 0;
def = factory->deferred<int>();
def.getPromise().onSuccess(successCb).onFailure(failCb);
EXPECT_TRUE(def.tryFail(std::make_exception_ptr(std::logic_error{"error"})));
EXPECT_FALSE(def.tryResolve(43));
EXPECT_FALSE(def.tryResolve(val));
EXPECT_FALSE(def.tryFail(std::logic_error{"error"}));
EXPECT_FALSE(def.tryFail(std::make_exception_ptr(std::logic_error{"error"})));
factory->wait();
EXPECT_EQ(failCount.load(), 1);
EXPECT_EQ(successCount.load(), 0);
}
#ifdef __clang__
#pragma clang diagnostic pop
#endif