TEST(Future, unwrap) { Promise<int> a; Promise<int> b; auto fa = a.getFuture(); auto fb = b.getFuture(); bool flag1 = false; bool flag2 = false; // do a, then do b, and get the result of a + b. Future<int> f = fa.then([&](Try<int>&& ta) { auto va = ta.value(); flag1 = true; return fb.then([va, &flag2](Try<int>&& tb) { flag2 = true; return va + tb.value(); }); }); EXPECT_FALSE(flag1); EXPECT_FALSE(flag2); EXPECT_FALSE(f.isReady()); a.setValue(3); EXPECT_TRUE(flag1); EXPECT_FALSE(flag2); EXPECT_FALSE(f.isReady()); b.setValue(4); EXPECT_TRUE(flag1); EXPECT_TRUE(flag2); EXPECT_EQ(7, f.value()); }
void test_promise() { Promise<bool> p; auto f = p.getFuture(); try { p.getFuture(); ok(false, "should throw"); } catch (const std::logic_error& exc) { ok(!strcmp(exc.what(), "Future already obtained"), "can't getFuture twice"); } ok(!f.isReady(), "not yet ready"); p.setValue(true); try { p.setValue(false); ok(false, "should throw"); } catch (const std::logic_error& exc) { ok(!strcmp(exc.what(), "Promise already fulfilled"), "can't setValue twice"); } ok(f.isReady(), "now ready"); ok(f.get() == true, "got our true value"); Promise<std::string> s; s.setException(std::make_exception_ptr(std::runtime_error("boo"))); auto f2 = s.getFuture(); ok(f2.result().hasError(), "holds an error"); try { f2.get(); } catch (const std::runtime_error& exc) { ok(!strcmp(exc.what(), "boo"), "has boo string"); } }
TEST(Collect, collectVariadicWithException) { Promise<bool> pb; Promise<int> pi; Future<bool> fb = pb.getFuture(); Future<int> fi = pi.getFuture(); auto f = collect(std::move(fb), std::move(fi)); pb.setValue(true); EXPECT_FALSE(f.isReady()); pi.setException(eggs); EXPECT_TRUE(f.isReady()); EXPECT_TRUE(f.getTry().hasException()); EXPECT_THROW(f.get(), eggs_t); }
TEST(Promise, setWith) { { Promise<int> p; auto f = p.getFuture(); p.setWith([] { return 42; }); EXPECT_EQ(42, f.value()); } { Promise<int> p; auto f = p.getFuture(); p.setWith([]() -> int { throw eggs; }); EXPECT_THROW(f.value(), eggs_t); } }
TEST(Future, finishBigLambda) { auto x = std::make_shared<int>(0); // bulk_data, to be captured in the lambda passed to Future::then. // This is meant to force that the lambda can't be stored inside // the Future object. std::array<char, sizeof(detail::Core<int>)> bulk_data = {0}; // suppress gcc warning about bulk_data not being used EXPECT_EQ(bulk_data[0], 0); Promise<int> p; auto f = p.getFuture().then([x, bulk_data](Try<int>&& t) { *x = t.value(); }); // The callback hasn't executed EXPECT_EQ(0, *x); // The callback has a reference to x EXPECT_EQ(2, x.use_count()); p.setValue(42); // the callback has executed EXPECT_EQ(42, *x); // the callback has been destructed // and has released its reference to x EXPECT_EQ(1, x.use_count()); }
TEST(Future, isReady) { Promise<int> p; auto f = p.getFuture(); EXPECT_FALSE(f.isReady()); p.setValue(42); EXPECT_TRUE(f.isReady()); }
TEST(Future, toUnitWhileInProgress) { Promise<int> p; Future<Unit> fu = p.getFuture().unit(); EXPECT_FALSE(fu.isReady()); p.setValue(42); EXPECT_TRUE(fu.isReady()); }
TEST(Interrupt, interruptThenHandle) { Promise<int> p; bool flag = false; p.getFuture().cancel(); p.setInterruptHandler([&](const exception_wrapper& /* e */) { flag = true; }); EXPECT_TRUE(flag); }
TEST(Interrupt, cancel) { Promise<Unit> p; p.setInterruptHandler([&](const exception_wrapper& e) { EXPECT_THROW(e.throw_exception(), FutureCancellation); }); p.getFuture().cancel(); }
TEST(Wait, wait) { Promise<int> p; Future<int> f = p.getFuture(); std::atomic<bool> flag{false}; std::atomic<int> result{1}; std::atomic<std::thread::id> id; std::thread t([&](Future<int>&& tf){ auto n = tf.then([&](Try<int> && t) { id = std::this_thread::get_id(); return t.value(); }); flag = true; result.store(n.wait().value()); }, std::move(f) ); while(!flag){} EXPECT_EQ(result.load(), 1); p.setValue(42); t.join(); // validate that the callback ended up executing in this thread, which // is more to ensure that this test actually tests what it should EXPECT_EQ(id, std::this_thread::get_id()); EXPECT_EQ(result.load(), 42); }
TEST(Future, thenTry) { bool flag = false; makeFuture<int>(42).then([&](Try<int>&& t) { flag = true; EXPECT_EQ(42, t.value()); }); EXPECT_TRUE(flag); flag = false; makeFuture<int>(42) .then([](Try<int>&& t) { return t.value(); }) .then([&](Try<int>&& t) { flag = true; EXPECT_EQ(42, t.value()); }); EXPECT_TRUE(flag); flag = false; makeFuture().then([&](Try<Unit>&& t) { flag = true; t.value(); }); EXPECT_TRUE(flag); flag = false; Promise<Unit> p; auto f = p.getFuture().then([&](Try<Unit>&& /* t */) { flag = true; }); EXPECT_FALSE(flag); EXPECT_FALSE(f.isReady()); p.setValue(); EXPECT_TRUE(flag); EXPECT_TRUE(f.isReady()); }
TEST(Context, basic) { // Start a new context folly::RequestContextScopeGuard rctx; EXPECT_EQ(nullptr, RequestContext::get()->getContextData("test")); // Set some test data RequestContext::get()->setContextData( "test", std::unique_ptr<TestData>(new TestData(10))); // Start a future Promise<Unit> p; auto future = p.getFuture().then([&]{ // Check that the context followed the future EXPECT_TRUE(RequestContext::get() != nullptr); auto a = dynamic_cast<TestData*>( RequestContext::get()->getContextData("test")); auto data = a->data_; EXPECT_EQ(10, data); }); // Clear the context RequestContext::setContext(nullptr); EXPECT_EQ(nullptr, RequestContext::get()->getContextData("test")); // Fulfill the promise p.setValue(); }
TEST(NonCopyableLambda, unique_ptr) { Promise<Unit> promise; auto int_ptr = std::make_unique<int>(1); EXPECT_EQ(*int_ptr, 1); auto future = promise.getFuture().thenValue(std::bind( [](std::unique_ptr<int>& p, folly::Unit) mutable { ++*p; return std::move(p); }, std::move(int_ptr), std::placeholders::_1)); // The previous statement can be simplified in C++14: // auto future = // promise.getFuture().thenValue([int_ptr = std::move(int_ptr)]( // auto&&) mutable { // ++*int_ptr; // return std::move(int_ptr); // }); EXPECT_FALSE(future.isReady()); promise.setValue(); EXPECT_TRUE(future.isReady()); EXPECT_EQ(*std::move(future).get(), 2); }
TEST(Promise, isFulfilledWithFuture) { Promise<int> p; auto f = p.getFuture(); // so core_ will become null EXPECT_FALSE(p.isFulfilled()); p.setValue(42); // after here EXPECT_TRUE(p.isFulfilled()); }
TEST(Interrupt, interruptAfterFulfilNoop) { Promise<Unit> p; bool flag = false; p.setInterruptHandler([&](const exception_wrapper& /* e */) { flag = true; }); p.setValue(); p.getFuture().cancel(); EXPECT_FALSE(flag); }
TEST(Interrupt, raise) { using eggs_t = std::runtime_error; Promise<Unit> p; p.setInterruptHandler([&](const exception_wrapper& e) { EXPECT_THROW(e.throw_exception(), eggs_t); }); p.getFuture().raise(eggs_t("eggs")); }
TEST(Timekeeper, futureWithinThrows) { Promise<int> p; auto f = p.getFuture() .within(one_ms) .onError([](TimedOut&) { return -1; }); EXPECT_EQ(-1, f.get()); }
TEST(Collect, collectVariadic) { Promise<bool> pb; Promise<int> pi; Future<bool> fb = pb.getFuture(); Future<int> fi = pi.getFuture(); bool flag = false; collect(std::move(fb), std::move(fi)) .then([&](std::tuple<bool, int> tup) { flag = true; EXPECT_EQ(std::get<0>(tup), true); EXPECT_EQ(std::get<1>(tup), 42); }); pb.setValue(true); EXPECT_FALSE(flag); pi.setValue(42); EXPECT_TRUE(flag); }
TEST(SemiFuture, DeferWithGetTimedGet) { std::atomic<int> innerResult{0}; Promise<folly::Unit> p; auto f = p.getFuture(); auto sf = std::move(f).semi().defer([&]() { innerResult = 17; }); EXPECT_THROW(std::move(sf).get(std::chrono::milliseconds(100)), TimedOut); ASSERT_EQ(innerResult, 0); }
TEST(Timekeeper, futureWithinFinishesInTime) { Promise<int> p; auto f = p.getFuture() .within(std::chrono::minutes(1)) .onError([&](TimedOut&){ return -1; }); p.setValue(42); EXPECT_EQ(42, f.get()); }
TEST(Interrupt, withinTimedOut) { Promise<int> p; Baton<> done; p.setInterruptHandler([&](const exception_wrapper& /* e */) { done.post(); }); p.getFuture().within(std::chrono::milliseconds(1)); // Give it 100ms to time out and call the interrupt handler auto t = std::chrono::steady_clock::now() + std::chrono::milliseconds(100); EXPECT_TRUE(done.timed_wait(t)); }
TEST(Interrupt, secondInterruptNoop) { Promise<Unit> p; int count = 0; p.setInterruptHandler([&](const exception_wrapper& /* e */) { count++; }); auto f = p.getFuture(); f.cancel(); f.cancel(); EXPECT_EQ(1, count); }
TEST(Promise, setException) { { Promise<Unit> p; auto f = p.getFuture(); p.setException(eggs); EXPECT_THROW(f.value(), eggs_t); } { Promise<Unit> p; auto f = p.getFuture(); try { throw eggs; } catch (...) { p.setException(exception_wrapper(std::current_exception())); } EXPECT_THROW(f.value(), eggs_t); } }
TEST(Timekeeper, futureWithinHandlesNullTimekeeperSingleton) { Singleton<ThreadWheelTimekeeper>::make_mock([] { return nullptr; }); SCOPE_EXIT { Singleton<ThreadWheelTimekeeper>::make_mock(); }; Promise<int> p; auto f = p.getFuture().within(one_ms); EXPECT_THROW(f.get(), NoTimekeeper); }
TEST(SemiFuture, DeferWithGetTimedWait) { Promise<folly::Unit> p; auto f = p.getFuture(); auto sf = std::move(f).semi().defer([&]() { return 17; }); ASSERT_FALSE(sf.isReady()); sf.wait(std::chrono::milliseconds(100)); ASSERT_FALSE(sf.isReady()); p.setValue(); ASSERT_EQ(std::move(sf).get(), 17); }
TEST(Collect, collectAllVariadicWithException) { Promise<bool> pb; Promise<int> pi; Future<bool> fb = pb.getFuture(); Future<int> fi = pi.getFuture(); bool flag = false; collectAll(std::move(fb), std::move(fi)) .then([&](std::tuple<Try<bool>, Try<int>> tup) { flag = true; EXPECT_TRUE(std::get<0>(tup).hasValue()); EXPECT_EQ(std::get<0>(tup).value(), true); EXPECT_TRUE(std::get<1>(tup).hasException()); EXPECT_THROW(std::get<1>(tup).value(), eggs_t); }); pb.setValue(true); EXPECT_FALSE(flag); pi.setException(eggs); EXPECT_TRUE(flag); }
TEST(Collect, collectAllVariadicReferences) { Promise<bool> pb; Promise<int> pi; Future<bool> fb = pb.getFuture(); Future<int> fi = pi.getFuture(); bool flag = false; collectAll(fb, fi) .then([&](std::tuple<Try<bool>, Try<int>> tup) { flag = true; EXPECT_TRUE(std::get<0>(tup).hasValue()); EXPECT_EQ(std::get<0>(tup).value(), true); EXPECT_TRUE(std::get<1>(tup).hasValue()); EXPECT_EQ(std::get<1>(tup).value(), 42); }); pb.setValue(true); EXPECT_FALSE(flag); pi.setValue(42); EXPECT_TRUE(flag); }
TEST(SemiFuture, SimpleDeferWithValue) { std::atomic<int> innerResult{0}; Promise<int> p; auto f = p.getFuture(); auto sf = std::move(f).semi().defer([&](int a) { innerResult = a; }); p.setValue(7); // Run "F" here inline in the calling thread std::move(sf).get(); ASSERT_EQ(innerResult, 7); }
// Makes sure that the unwrap call also works when the promise was not yet // fulfilled, and that the returned Future<T> becomes ready once the promise // is fulfilled. TEST(Unwrap, futureNotReady) { Promise<Future<int>> p; Future<Future<int>> future = p.getFuture(); Future<int> unwrapped = future.unwrap(); // Sanity - should not be ready before the promise is fulfilled. ASSERT_FALSE(unwrapped.isReady()); // Fulfill the promise and make sure the unwrapped future is now ready. p.setValue(makeFuture(5484)); ASSERT_TRUE(unwrapped.isReady()); EXPECT_EQ(5484, unwrapped.value()); }
void test_thread() { Promise<std::string> p; std::thread thr([&p] { std::this_thread::sleep_for(std::chrono::milliseconds(10)); p.setValue("done"); }); auto f = p.getFuture(); ok(f.get() == "done", "done in thread"); thr.join(); }