+
+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(Future, via_then_get_was_racy) {
+ ThreadExecutor x;
+ std::unique_ptr<int> val = folly::via(&x)
+ .then([] { return folly::make_unique<int>(42); })
+ .get();
+ ASSERT_TRUE(!!val);
+ EXPECT_EQ(42, *val);
+}
+
+TEST(Future, ensure) {
+ size_t count = 0;
+ auto cob = [&]{ count++; };
+ auto f = makeFuture(42)
+ .ensure(cob)
+ .then([](int) { throw std::runtime_error("ensure"); })
+ .ensure(cob);
+
+ EXPECT_THROW(f.get(), std::runtime_error);
+ EXPECT_EQ(2, count);
+}
+
+TEST(Future, willEqual) {
+ //both p1 and p2 already fulfilled
+ {
+ Promise<int> p1;
+ Promise<int> p2;
+ p1.setValue(27);
+ p2.setValue(27);
+ auto f1 = p1.getFuture();
+ auto f2 = p2.getFuture();
+ EXPECT_TRUE(f1.willEqual(f2).get());
+ }{
+ Promise<int> p1;
+ Promise<int> p2;
+ p1.setValue(27);
+ p2.setValue(36);
+ auto f1 = p1.getFuture();
+ auto f2 = p2.getFuture();
+ EXPECT_FALSE(f1.willEqual(f2).get());
+ }
+ //both p1 and p2 not yet fulfilled
+ {
+ Promise<int> p1;
+ Promise<int> p2;
+ auto f1 = p1.getFuture();
+ auto f2 = p2.getFuture();
+ auto f3 = f1.willEqual(f2);
+ p1.setValue(27);
+ p2.setValue(27);
+ EXPECT_TRUE(f3.get());
+ }{
+ Promise<int> p1;
+ Promise<int> p2;
+ auto f1 = p1.getFuture();
+ auto f2 = p2.getFuture();
+ auto f3 = f1.willEqual(f2);
+ p1.setValue(27);
+ p2.setValue(36);
+ EXPECT_FALSE(f3.get());
+ }
+ //p1 already fulfilled, p2 not yet fulfilled
+ {
+ Promise<int> p1;
+ Promise<int> p2;
+ p1.setValue(27);
+ auto f1 = p1.getFuture();
+ auto f2 = p2.getFuture();
+ auto f3 = f1.willEqual(f2);
+ p2.setValue(27);
+ EXPECT_TRUE(f3.get());
+ }{
+ Promise<int> p1;
+ Promise<int> p2;
+ p1.setValue(27);
+ auto f1 = p1.getFuture();
+ auto f2 = p2.getFuture();
+ auto f3 = f1.willEqual(f2);
+ p2.setValue(36);
+ EXPECT_FALSE(f3.get());
+ }
+ //p2 already fulfilled, p1 not yet fulfilled
+ {
+ Promise<int> p1;
+ Promise<int> p2;
+ p2.setValue(27);
+ auto f1 = p1.getFuture();
+ auto f2 = p2.getFuture();
+ auto f3 = f1.willEqual(f2);
+ p1.setValue(27);
+ EXPECT_TRUE(f3.get());
+ }{
+ Promise<int> p1;
+ Promise<int> p2;
+ p2.setValue(36);
+ auto f1 = p1.getFuture();
+ auto f2 = p2.getFuture();
+ auto f3 = f1.willEqual(f2);
+ p1.setValue(27);
+ EXPECT_FALSE(f3.get());
+ }
+}
+
+// Unwrap tests.
+
+// A simple scenario for the unwrap call, when the promise was fulfilled
+// before calling to unwrap.
+TEST(Future, Unwrap_SimpleScenario) {
+ Future<int> encapsulated_future = makeFuture(5484);
+ Future<Future<int>> future = makeFuture(std::move(encapsulated_future));
+ EXPECT_EQ(5484, future.unwrap().value());
+}
+
+// Makes sure that unwrap() works when chaning Future's commands.
+TEST(Future, Unwrap_ChainCommands) {
+ Future<Future<int>> future = makeFuture(makeFuture(5484));
+ auto unwrapped = future.unwrap().then([](int i){ return i; });
+ EXPECT_EQ(5484, unwrapped.value());
+}
+
+// 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(Future, 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(Reduce, Basic) {
+ auto makeFutures = [](int count) {
+ std::vector<Future<int>> fs;
+ for (int i = 1; i <= count; ++i) {
+ fs.emplace_back(makeFuture(i));
+ }
+ return fs;
+ };
+
+ // Empty (Try)
+ {
+ auto fs = makeFutures(0);
+
+ Future<double> f1 = reduce(fs, 1.2,
+ [](double a, Try<int>&& b){
+ return a + *b + 0.1;
+ });
+ EXPECT_EQ(1.2, f1.get());
+ }
+
+ // One (Try)
+ {
+ auto fs = makeFutures(1);
+
+ Future<double> f1 = reduce(fs, 0.0,
+ [](double a, Try<int>&& b){
+ return a + *b + 0.1;
+ });
+ EXPECT_EQ(1.1, f1.get());
+ }
+
+ // Returning values (Try)
+ {
+ auto fs = makeFutures(3);
+
+ Future<double> f1 = reduce(fs, 0.0,
+ [](double a, Try<int>&& b){
+ return a + *b + 0.1;
+ });
+ EXPECT_EQ(6.3, f1.get());
+ }
+
+ // Returning values
+ {
+ auto fs = makeFutures(3);
+
+ Future<double> f1 = reduce(fs, 0.0,
+ [](double a, int&& b){
+ return a + b + 0.1;
+ });
+ EXPECT_EQ(6.3, f1.get());
+ }
+
+ // Returning futures (Try)
+ {
+ auto fs = makeFutures(3);
+
+ Future<double> f2 = reduce(fs, 0.0,
+ [](double a, Try<int>&& b){
+ return makeFuture<double>(a + *b + 0.1);
+ });
+ EXPECT_EQ(6.3, f2.get());
+ }
+
+ // Returning futures
+ {
+ auto fs = makeFutures(3);
+
+ Future<double> f2 = reduce(fs, 0.0,
+ [](double a, int&& b){
+ return makeFuture<double>(a + b + 0.1);
+ });
+ EXPECT_EQ(6.3, f2.get());
+ }
+}
+
+TEST(Reduce, Chain) {
+ auto makeFutures = [](int count) {
+ std::vector<Future<int>> fs;
+ for (int i = 1; i <= count; ++i) {
+ fs.emplace_back(makeFuture(i));
+ }
+ return fs;
+ };
+
+ {
+ auto f = collectAll(makeFutures(3)).reduce(0, [](int a, Try<int>&& b){
+ return a + *b;
+ });
+ EXPECT_EQ(6, f.get());
+ }
+ {
+ auto f = collect(makeFutures(3)).reduce(0, [](int a, int&& b){
+ return a + b;
+ });
+ EXPECT_EQ(6, f.get());
+ }
+}
+
+TEST(Reduce, Streaming) {
+ {
+ std::vector<Future<int>> fs;
+ fs.push_back(makeFuture(1));
+ fs.push_back(makeFuture(2));
+ fs.push_back(makeFuture(3));
+
+ Future<double> f = unorderedReduce(fs.begin(), fs.end(), 0.0,
+ [](double a, int&& b){
+ return double(b);
+ });
+ EXPECT_EQ(3.0, f.get());
+ }
+ {
+ Promise<int> p1;
+ Promise<int> p2;
+ Promise<int> p3;
+
+ std::vector<Future<int>> fs;
+ fs.push_back(p1.getFuture());
+ fs.push_back(p2.getFuture());
+ fs.push_back(p3.getFuture());
+
+ Future<double> f = unorderedReduce(fs.begin(), fs.end(), 0.0,
+ [](double a, int&& b){
+ return double(b);
+ });
+ p3.setValue(3);
+ p2.setValue(2);
+ p1.setValue(1);
+ EXPECT_EQ(1.0, f.get());
+ }
+}
+
+TEST(Reduce, StreamingException) {
+ Promise<int> p1;
+ Promise<int> p2;
+ Promise<int> p3;
+
+ std::vector<Future<int>> fs;
+ fs.push_back(p1.getFuture());
+ fs.push_back(p2.getFuture());
+ fs.push_back(p3.getFuture());
+
+ Future<double> f = unorderedReduce(fs.begin(), fs.end(), 0.0,
+ [](double a, int&& b){
+ return b + 0.0;
+ });
+ p3.setValue(3);
+ p2.setException(exception_wrapper(std::runtime_error("blah")));
+ p1.setValue(1);
+ EXPECT_THROW(f.get(), std::runtime_error);
+}
+
+TEST(Map, Basic) {
+ Promise<int> p1;
+ Promise<int> p2;
+ Promise<int> p3;
+
+ std::vector<Future<int>> fs;
+ fs.push_back(p1.getFuture());
+ fs.push_back(p2.getFuture());
+ fs.push_back(p3.getFuture());
+
+ int c = 0;
+ std::vector<Future<void>> fs2 = futures::map(fs, [&](int i){
+ c += i;
+ });
+
+ // Ensure we call the callbacks as the futures complete regardless of order
+ p2.setValue(1);
+ EXPECT_EQ(1, c);
+ p3.setValue(1);
+ EXPECT_EQ(2, c);
+ p1.setValue(1);
+ EXPECT_EQ(3, c);
+
+ EXPECT_TRUE(collect(fs2).isReady());
+}