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(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()); }
TEST(Promise, isFulfilled) { Promise<int> p; EXPECT_FALSE(p.isFulfilled()); p.setValue(42); EXPECT_TRUE(p.isFulfilled()); }
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(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(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(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); }
FutureSync<void> TransportSocketCache::disconnect(MessageSocketPtr socket) { Promise<void> promiseSocketRemoved; { auto syncDisconnectInfos = _disconnectInfos.synchronize(); // TODO: Remove Promise<void>{} when get rid of VS2013. syncDisconnectInfos->push_back(DisconnectInfo{socket, Promise<void>{}}); promiseSocketRemoved = syncDisconnectInfos->back().promiseSocketRemoved; } // We wait that the socket has been disconnected _and_ the `disconnected` // signal has been received by the cache. FutureBarrier<void> barrier; barrier.addFuture(promiseSocketRemoved.future()); barrier.addFuture(socket->disconnect()); Promise<void> promise; return barrier.future().then([=](const std::vector<Future<void>>& v) mutable { const auto isInError = [](const Future<void>& f) { return f.hasError(); }; if (std::any_of(begin(v), end(v), isInError)) { promise.setError("disconnect error"); return; } promise.setValue(0); }); }
TEST(Future, toUnitWhileInProgress) { Promise<int> p; Future<Unit> fu = p.getFuture().unit(); EXPECT_FALSE(fu.isReady()); p.setValue(42); EXPECT_TRUE(fu.isReady()); }
TEST(SemiFuture, SimpleValue) { Promise<int> p; auto sf = p.getSemiFuture(); p.setValue(3); auto v = std::move(sf).value(); ASSERT_EQ(v, 3); }
TEST(Future, isReady) { Promise<int> p; auto f = p.getFuture(); EXPECT_FALSE(f.isReady()); p.setValue(42); EXPECT_TRUE(f.isReady()); }
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(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(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(SemiFuture, SimpleResult) { EventBase e2; Promise<int> p; auto sf = p.getSemiFuture(); p.setValue(3); auto v = std::move(sf).result(); ASSERT_EQ(v.value(), 3); }
TEST(SemiFuture, SimpleTimedWait) { Promise<folly::Unit> p; auto sf = p.getSemiFuture(); sf.wait(std::chrono::milliseconds(100)); EXPECT_FALSE(sf.isReady()); p.setValue(); EXPECT_TRUE(sf.isReady()); }
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(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(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); }
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, 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); }
// 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()); }
TEST(SemiFuture, DeferWithVia) { std::atomic<int> innerResult{0}; EventBase e2; Promise<folly::Unit> p; auto f = p.getFuture(); auto sf = std::move(f).semi().defer([&]() { innerResult = 17; }); // Run "F" here inline in the calling thread auto tf = std::move(sf).via(&e2); p.setValue(); tf.getVia(&e2); ASSERT_EQ(innerResult, 17); }
TEST(Timekeeper, futureGetBeforeTimeout) { Promise<int> p; auto t = std::thread([&]{ p.setValue(42); }); // Technically this is a race and if the test server is REALLY overloaded // and it takes more than a second to do that thread it could be flaky. But // I want a low timeout (in human terms) so if this regresses and someone // runs it by hand they're not sitting there forever wondering why it's // blocked, and get a useful error message instead. If it does get flaky, // empirically increase the timeout to the point where it's very improbable. EXPECT_EQ(42, p.getFuture().get(std::chrono::seconds(2))); t.join(); }
TEST(SharedPromise, splitFutureSuccess) { Promise<int> p; SharedPromise<int> sp(p.getFuture()); auto f1 = sp.getFuture(); EXPECT_FALSE(f1.isReady()); p.setValue(1); EXPECT_TRUE(f1.isReady()); EXPECT_TRUE(f1.hasValue()); auto f2 = sp.getFuture(); EXPECT_TRUE(f2.isReady()); EXPECT_TRUE(f2.hasValue()); }
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(); }
TEST(Future, thenDynamic) { // folly::dynamic has a constructor that takes any T, this test makes // sure that we call the then lambda with folly::dynamic and not // Try<folly::dynamic> because that then fails to compile Promise<folly::dynamic> p; Future<folly::dynamic> f = p.getFuture().then( [](const folly::dynamic& d) { return folly::dynamic(d.asInt() + 3); } ); p.setValue(2); EXPECT_EQ(f.get(), 5); }
TEST(NonCopyableLambda, FunctionConst) { Promise<int> promise; Function<int(int) const> callback = [](int x) { return x + 1; }; auto future = promise.getFuture().thenValue(std::move(callback)); EXPECT_THROW(callback(0), std::bad_function_call); EXPECT_FALSE(future.isReady()); promise.setValue(100); EXPECT_TRUE(future.isReady()); EXPECT_EQ(std::move(future).get(), 101); }
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(SemiFuture, MakeSemiFutureFromReadyFuture) { Promise<int> p; auto f = p.getSemiFuture(); EXPECT_FALSE(f.isReady()); p.setValue(42); EXPECT_TRUE(f.isReady()); }