collect()
[folly.git] / folly / futures / test / FutureTest.cpp
index 19dd1fa8349df55a5927366c5f26b5cca94f86ac..82ac1f175caf1dc38350273b5471587eb8780f3a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 Facebook, Inc.
+ * Copyright 2015 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@
 #include <folly/futures/Future.h>
 #include <folly/futures/ManualExecutor.h>
 #include <folly/futures/DrivableExecutor.h>
+#include <folly/dynamic.h>
 #include <folly/MPMCQueue.h>
 
 #include <folly/io/async/EventBase.h>
@@ -63,7 +64,9 @@ class ThreadExecutor : public Executor {
 
  public:
   explicit ThreadExecutor(size_t n = 1024)
-    : funcs(n), worker(std::bind(&ThreadExecutor::work, this)) {}
+    : funcs(n) {
+    worker = std::thread(std::bind(&ThreadExecutor::work, this));
+  }
 
   ~ThreadExecutor() {
     done = true;
@@ -88,7 +91,7 @@ static eggs_t eggs("eggs");
 TEST(Future, coreSize) {
   // If this number goes down, it's fine!
   // If it goes up, please seek professional advice ;-)
-  EXPECT_EQ(128, sizeof(detail::Core<void>));
+  EXPECT_EQ(192, sizeof(detail::Core<void>));
 }
 
 // Future
@@ -257,6 +260,66 @@ TEST(Future, onError) {
       .onError([&] (eggs_t& e) { throw e; return makeFuture<int>(-1); });
     EXPECT_THROW(f.value(), eggs_t);
   }
+
+  // exception_wrapper, return Future<T>
+  {
+    auto f = makeFuture()
+      .then([] { throw eggs; })
+      .onError([&] (exception_wrapper e) { flag(); return makeFuture(); });
+    EXPECT_FLAG();
+    EXPECT_NO_THROW(f.value());
+  }
+
+  // exception_wrapper, return Future<T> but throw
+  {
+    auto f = makeFuture()
+      .then([]{ throw eggs; return 0; })
+      .onError([&] (exception_wrapper e) {
+        flag();
+        throw eggs;
+        return makeFuture<int>(-1);
+      });
+    EXPECT_FLAG();
+    EXPECT_THROW(f.value(), eggs_t);
+  }
+
+  // exception_wrapper, return T
+  {
+    auto f = makeFuture()
+      .then([]{ throw eggs; return 0; })
+      .onError([&] (exception_wrapper e) {
+        flag();
+        return -1;
+      });
+    EXPECT_FLAG();
+    EXPECT_EQ(-1, f.value());
+  }
+
+  // exception_wrapper, return T but throw
+  {
+    auto f = makeFuture()
+      .then([]{ throw eggs; return 0; })
+      .onError([&] (exception_wrapper e) {
+        flag();
+        throw eggs;
+        return -1;
+      });
+    EXPECT_FLAG();
+    EXPECT_THROW(f.value(), eggs_t);
+  }
+
+  // const exception_wrapper&
+  {
+    auto f = makeFuture()
+      .then([] { throw eggs; })
+      .onError([&] (const exception_wrapper& e) {
+        flag();
+        return makeFuture();
+      });
+    EXPECT_FLAG();
+    EXPECT_NO_THROW(f.value());
+  }
+
 }
 
 TEST(Future, try) {
@@ -554,17 +617,17 @@ TEST(Promise, setException) {
   }
 }
 
-TEST(Promise, fulfil) {
+TEST(Promise, setWith) {
   {
     Promise<int> p;
     auto f = p.getFuture();
-    p.fulfil([] { return 42; });
+    p.setWith([] { return 42; });
     EXPECT_EQ(42, f.value());
   }
   {
     Promise<int> p;
     auto f = p.getFuture();
-    p.fulfil([]() -> int { throw eggs; });
+    p.setWith([]() -> int { throw eggs; });
     EXPECT_THROW(f.value(), eggs_t);
   }
 }
@@ -703,6 +766,118 @@ TEST(Future, whenAll) {
   }
 }
 
+TEST(Future, collect) {
+  // success case
+  {
+    vector<Promise<int>> promises(10);
+    vector<Future<int>> futures;
+
+    for (auto& p : promises)
+      futures.push_back(p.getFuture());
+
+    auto allf = collect(futures.begin(), futures.end());
+
+    random_shuffle(promises.begin(), promises.end());
+    for (auto& p : promises) {
+      EXPECT_FALSE(allf.isReady());
+      p.setValue(42);
+    }
+
+    EXPECT_TRUE(allf.isReady());
+    for (auto i : allf.value()) {
+      EXPECT_EQ(42, i);
+    }
+  }
+
+  // failure case
+  {
+    vector<Promise<int>> promises(10);
+    vector<Future<int>> futures;
+
+    for (auto& p : promises)
+      futures.push_back(p.getFuture());
+
+    auto allf = collect(futures.begin(), futures.end());
+
+    random_shuffle(promises.begin(), promises.end());
+    for (int i = 0; i < 10; i++) {
+      if (i < 5) {
+        // everthing goes well so far...
+        EXPECT_FALSE(allf.isReady());
+        promises[i].setValue(42);
+      } else if (i == 5) {
+        // short circuit with an exception
+        EXPECT_FALSE(allf.isReady());
+        promises[i].setException(eggs);
+        EXPECT_TRUE(allf.isReady());
+      } else if (i < 8) {
+        // don't blow up on further values
+        EXPECT_TRUE(allf.isReady());
+        promises[i].setValue(42);
+      } else {
+        // don't blow up on further exceptions
+        EXPECT_TRUE(allf.isReady());
+        promises[i].setException(eggs);
+      }
+    }
+
+    EXPECT_THROW(allf.value(), eggs_t);
+  }
+
+  // void futures success case
+  {
+    vector<Promise<void>> promises(10);
+    vector<Future<void>> futures;
+
+    for (auto& p : promises)
+      futures.push_back(p.getFuture());
+
+    auto allf = collect(futures.begin(), futures.end());
+
+    random_shuffle(promises.begin(), promises.end());
+    for (auto& p : promises) {
+      EXPECT_FALSE(allf.isReady());
+      p.setValue();
+    }
+
+    EXPECT_TRUE(allf.isReady());
+  }
+
+  // void futures failure case
+  {
+    vector<Promise<void>> promises(10);
+    vector<Future<void>> futures;
+
+    for (auto& p : promises)
+      futures.push_back(p.getFuture());
+
+    auto allf = collect(futures.begin(), futures.end());
+
+    random_shuffle(promises.begin(), promises.end());
+    for (int i = 0; i < 10; i++) {
+      if (i < 5) {
+        // everthing goes well so far...
+        EXPECT_FALSE(allf.isReady());
+        promises[i].setValue();
+      } else if (i == 5) {
+        // short circuit with an exception
+        EXPECT_FALSE(allf.isReady());
+        promises[i].setException(eggs);
+        EXPECT_TRUE(allf.isReady());
+      } else if (i < 8) {
+        // don't blow up on further values
+        EXPECT_TRUE(allf.isReady());
+        promises[i].setValue();
+      } else {
+        // don't blow up on further exceptions
+        EXPECT_TRUE(allf.isReady());
+        promises[i].setException(eggs);
+      }
+    }
+
+    EXPECT_THROW(allf.value(), eggs_t);
+  }
+}
 
 TEST(Future, whenAny) {
   {
@@ -1177,7 +1352,7 @@ TEST(Future, getFuture_after_setValue) {
 
 TEST(Future, getFuture_after_setException) {
   Promise<void> p;
-  p.fulfil([]() -> void { throw std::logic_error("foo"); });
+  p.setWith([]() -> void { throw std::logic_error("foo"); });
   EXPECT_THROW(p.getFuture().value(), std::logic_error);
 }
 
@@ -1239,7 +1414,7 @@ TEST(Future, context) {
 
   EXPECT_EQ(nullptr, RequestContext::get()->getContextData("test"));
 
-  // Fulfil the promise
+  // Fulfill the promise
   p.setValue();
 }
 
@@ -1291,7 +1466,7 @@ TEST(Future, CircularDependencySharedPtrSelfReset) {
 
   ptr.reset();
 
-  promise.fulfil([]{return 1l;});
+  promise.setWith([]{return 1l;});
 }
 
 TEST(Future, Constructor) {
@@ -1309,6 +1484,20 @@ TEST(Future, ImplicitConstructor) {
   //auto f2 = []() -> Future<void> { }();
 }
 
+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)
@@ -1329,3 +1518,192 @@ TEST(Future, ensure) {
   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.begin(), fs.end(), 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.begin(), fs.end(), 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.begin(), fs.end(), 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.begin(), fs.end(), 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.begin(), fs.end(), 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.begin(), fs.end(), 0.0,
+      [](double a, int&& b){
+        return makeFuture<double>(a + b + 0.1);
+      });
+    EXPECT_EQ(6.3, f2.get());
+  }
+}