/*
- * Copyright 2016 Facebook, Inc.
+ * Copyright 2017 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* limitations under the License.
*/
-#include <gtest/gtest.h>
-
#include <folly/futures/Future.h>
+#include <folly/Unit.h>
#include <folly/Memory.h>
#include <folly/Executor.h>
#include <folly/dynamic.h>
#include <folly/Baton.h>
+#include <folly/portability/GTest.h>
#include <algorithm>
#include <atomic>
#include <string>
#include <thread>
#include <type_traits>
-#include <unistd.h>
using namespace folly;
// Future
+TEST(Future, futureDefaultCtor) {
+ Future<Unit>();
+}
+
+TEST(Future, futureToUnit) {
+ Future<Unit> fu = makeFuture(42).unit();
+ fu.value();
+ EXPECT_TRUE(makeFuture<int>(eggs).unit().hasException());
+}
+
+TEST(Future, voidFutureToUnit) {
+ Future<Unit> fu = makeFuture().unit();
+ fu.value();
+ EXPECT_TRUE(makeFuture<Unit>(eggs).unit().hasException());
+}
+
+TEST(Future, unitFutureToUnitIdentity) {
+ Future<Unit> fu = makeFuture(Unit{}).unit();
+ fu.value();
+ EXPECT_TRUE(makeFuture<Unit>(eggs).unit().hasException());
+}
+
+TEST(Future, toUnitWhileInProgress) {
+ Promise<int> p;
+ Future<Unit> fu = p.getFuture().unit();
+ EXPECT_FALSE(fu.isReady());
+ p.setValue(42);
+ EXPECT_TRUE(fu.isReady());
+}
+
+TEST(Future, makeFutureWithUnit) {
+ int count = 0;
+ Future<Unit> fu = makeFutureWith([&] { count++; });
+ EXPECT_EQ(1, count);
+}
+
TEST(Future, onError) {
bool theFlag = false;
auto flag = [&]{ theFlag = true; };
// Returned value propagates
{
- auto f = makeFuture().then([] {
+ auto f = makeFuture().then([]() -> int {
throw eggs;
- return 0;
}).onError([&](eggs_t& /* e */) { return 42; });
EXPECT_EQ(42, f.value());
}
// Returned future propagates
{
- auto f = makeFuture().then([] {
+ auto f = makeFuture().then([]() -> int {
throw eggs;
- return 0;
}).onError([&](eggs_t& /* e */) { return makeFuture<int>(42); });
EXPECT_EQ(42, f.value());
}
// Throw in callback
{
auto f = makeFuture()
- .then([] { throw eggs; return 0; })
- .onError([&] (eggs_t& e) { throw e; return -1; });
+ .then([]() -> int { throw eggs; })
+ .onError([&] (eggs_t& e) -> int { throw e; });
EXPECT_THROW(f.value(), eggs_t);
}
{
auto f = makeFuture()
- .then([] { throw eggs; return 0; })
- .onError([&] (eggs_t& e) { throw e; return makeFuture<int>(-1); });
+ .then([]() -> int { throw eggs; })
+ .onError([&] (eggs_t& e) -> Future<int> { throw e; });
EXPECT_THROW(f.value(), eggs_t);
}
// exception_wrapper, return Future<T> but throw
{
auto f = makeFuture()
- .then([] {
+ .then([]() -> int {
throw eggs;
- return 0;
})
- .onError([&](exception_wrapper /* e */) {
+ .onError([&](exception_wrapper /* e */) -> Future<int> {
flag();
throw eggs;
- return makeFuture<int>(-1);
});
EXPECT_FLAG();
EXPECT_THROW(f.value(), eggs_t);
// exception_wrapper, return T
{
auto f = makeFuture()
- .then([] {
+ .then([]() -> int {
throw eggs;
- return 0;
})
.onError([&](exception_wrapper /* e */) {
flag();
// exception_wrapper, return T but throw
{
auto f = makeFuture()
- .then([] {
+ .then([]() -> int {
throw eggs;
- return 0;
})
- .onError([&](exception_wrapper /* e */) {
+ .onError([&](exception_wrapper /* e */) -> int {
flag();
throw eggs;
- return -1;
});
EXPECT_FLAG();
EXPECT_THROW(f.value(), eggs_t);
// 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};
+ 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);
// slow test so I won't do that but if it ever fails, take it seriously, and
// run the test binary with "--gtest_repeat=10000 --gtest_filter=*detachRace"
// (Don't forget to enable ASAN)
- auto p = folly::make_unique<Promise<bool>>();
- auto f = folly::make_unique<Future<bool>>(p->getFuture());
+ auto p = std::make_unique<Promise<bool>>();
+ auto f = std::make_unique<Future<bool>>(p->getFuture());
folly::Baton<> baton;
std::thread t1([&]{
baton.post();
bool value;
};
+ Promise<int> p1, p2;
NewThreadExecutor e;
- RequestContext::create();
- RequestContext::get()->setContextData("key",
- folly::make_unique<MyRequestData>(true));
- auto checker = [](int lineno) {
- return [lineno](Try<int>&& /* t */) {
- auto d = static_cast<MyRequestData*>(
- RequestContext::get()->getContextData("key"));
- EXPECT_TRUE(d && d->value) << "on line " << lineno;
+ {
+ folly::RequestContextScopeGuard rctx;
+ RequestContext::get()->setContextData(
+ "key", std::make_unique<MyRequestData>(true));
+ auto checker = [](int lineno) {
+ return [lineno](Try<int>&& /* t */) {
+ auto d = static_cast<MyRequestData*>(
+ RequestContext::get()->getContextData("key"));
+ EXPECT_TRUE(d && d->value) << "on line " << lineno;
+ };
};
- };
-
- makeFuture(1).via(&e).then(checker(__LINE__));
- e.setHandlesPriorities();
- makeFuture(2).via(&e).then(checker(__LINE__));
+ makeFuture(1).via(&e).then(checker(__LINE__));
- Promise<int> p1, p2;
- p1.getFuture().then(checker(__LINE__));
+ e.setHandlesPriorities();
+ makeFuture(2).via(&e).then(checker(__LINE__));
- e.setThrowsOnAdd();
- p2.getFuture().via(&e).then(checker(__LINE__));
+ p1.getFuture().then(checker(__LINE__));
- RequestContext::create();
+ e.setThrowsOnAdd();
+ p2.getFuture().via(&e).then(checker(__LINE__));
+ }
+ // Assert that no RequestContext is set
+ EXPECT_FALSE(RequestContext::saveContext());
p1.setValue(3);
p2.setValue(4);
}
TEST(Future, makeFutureNoThrow) {
makeFuture().value();
}
+
+TEST(Future, invokeCallbackReturningValueAsRvalue) {
+ struct Foo {
+ int operator()(int x) & {
+ return x + 1;
+ }
+ int operator()(int x) const& {
+ return x + 2;
+ }
+ int operator()(int x) && {
+ return x + 3;
+ }
+ };
+
+ Foo foo;
+ Foo const cfoo;
+
+ // The callback will be copied when given as lvalue or const ref, and moved
+ // if provided as rvalue. Either way, it should be executed as rvalue.
+ EXPECT_EQ(103, makeFuture<int>(100).then(foo).value());
+ EXPECT_EQ(203, makeFuture<int>(200).then(cfoo).value());
+ EXPECT_EQ(303, makeFuture<int>(300).then(Foo()).value());
+}
+
+TEST(Future, invokeCallbackReturningFutureAsRvalue) {
+ struct Foo {
+ Future<int> operator()(int x) & {
+ return x + 1;
+ }
+ Future<int> operator()(int x) const& {
+ return x + 2;
+ }
+ Future<int> operator()(int x) && {
+ return x + 3;
+ }
+ };
+
+ Foo foo;
+ Foo const cfoo;
+
+ // The callback will be copied when given as lvalue or const ref, and moved
+ // if provided as rvalue. Either way, it should be executed as rvalue.
+ EXPECT_EQ(103, makeFuture<int>(100).then(foo).value());
+ EXPECT_EQ(203, makeFuture<int>(200).then(cfoo).value());
+ EXPECT_EQ(303, makeFuture<int>(300).then(Foo()).value());
+}