X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;ds=sidebyside;f=folly%2Ffutures%2Ftest%2FFutureTest.cpp;h=82ac1f175caf1dc38350273b5471587eb8780f3a;hb=162c972335772b3e972fa63a7f4499ff066cd17b;hp=ed696f9a65c497313212bbb716f063a6a6358970;hpb=42ab246273a3c4f63b3645207818d0ea698eadc0;p=folly.git diff --git a/folly/futures/test/FutureTest.cpp b/folly/futures/test/FutureTest.cpp index ed696f9a..82ac1f17 100644 --- a/folly/futures/test/FutureTest.cpp +++ b/folly/futures/test/FutureTest.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -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)); + EXPECT_EQ(192, sizeof(detail::Core)); } // Future @@ -257,6 +260,66 @@ TEST(Future, onError) { .onError([&] (eggs_t& e) { throw e; return makeFuture(-1); }); EXPECT_THROW(f.value(), eggs_t); } + + // exception_wrapper, return Future + { + auto f = makeFuture() + .then([] { throw eggs; }) + .onError([&] (exception_wrapper e) { flag(); return makeFuture(); }); + EXPECT_FLAG(); + EXPECT_NO_THROW(f.value()); + } + + // exception_wrapper, return Future but throw + { + auto f = makeFuture() + .then([]{ throw eggs; return 0; }) + .onError([&] (exception_wrapper e) { + flag(); + throw eggs; + return makeFuture(-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 p; auto f = p.getFuture(); - p.fulfil([] { return 42; }); + p.setWith([] { return 42; }); EXPECT_EQ(42, f.value()); } { Promise 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> promises(10); + vector> 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> promises(10); + vector> 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> promises(10); + vector> 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> promises(10); + vector> 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 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 { }(); } +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 because that then fails to compile + Promise p; + Future 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 val = folly::via(&x) @@ -1410,3 +1599,111 @@ TEST(Future, willEqual) { 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 encapsulated_future = makeFuture(5484); + Future> 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 = 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 becomes ready once the promise +// is fulfilled. +TEST(Future, Unwrap_FutureNotReady) { + Promise> p; + Future> future = p.getFuture(); + Future 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> fs; + for (int i = 1; i <= count; ++i) { + fs.emplace_back(makeFuture(i)); + } + return fs; + }; + + // Empty (Try) + { + auto fs = makeFutures(0); + + Future f1 = reduce(fs.begin(), fs.end(), 1.2, + [](double a, Try&& b){ + return a + *b + 0.1; + }); + EXPECT_EQ(1.2, f1.get()); + } + + // One (Try) + { + auto fs = makeFutures(1); + + Future f1 = reduce(fs.begin(), fs.end(), 0.0, + [](double a, Try&& b){ + return a + *b + 0.1; + }); + EXPECT_EQ(1.1, f1.get()); + } + + // Returning values (Try) + { + auto fs = makeFutures(3); + + Future f1 = reduce(fs.begin(), fs.end(), 0.0, + [](double a, Try&& b){ + return a + *b + 0.1; + }); + EXPECT_EQ(6.3, f1.get()); + } + + // Returning values + { + auto fs = makeFutures(3); + + Future 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 f2 = reduce(fs.begin(), fs.end(), 0.0, + [](double a, Try&& b){ + return makeFuture(a + *b + 0.1); + }); + EXPECT_EQ(6.3, f2.get()); + } + + // Returning futures + { + auto fs = makeFutures(3); + + Future f2 = reduce(fs.begin(), fs.end(), 0.0, + [](double a, int&& b){ + return makeFuture(a + b + 0.1); + }); + EXPECT_EQ(6.3, f2.get()); + } +}