X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2Ffutures%2Ftest%2FFutureTest.cpp;h=3322d2f4af52b5d40b028f99c2f5bbe138b0ddd2;hb=49f2f8b170dfee4871a84c57d6e149623126a2d3;hp=16c9f1f9f72c6fc37635c4e8e3b096b3d988f95b;hpb=e845ef57f1f368944123e4a50bd4d1616a363efa;p=folly.git diff --git a/folly/futures/test/FutureTest.cpp b/folly/futures/test/FutureTest.cpp index 16c9f1f9..3322d2f4 100644 --- a/folly/futures/test/FutureTest.cpp +++ b/folly/futures/test/FutureTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2015 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. @@ -14,13 +14,13 @@ * limitations under the License. */ -#include - #include +#include #include #include #include #include +#include #include #include @@ -29,7 +29,6 @@ #include #include #include -#include using namespace folly; @@ -41,6 +40,51 @@ static eggs_t eggs("eggs"); // Future +TEST(Future, futureDefaultCtor) { + Future(); +} + +TEST(Future, futureToUnit) { + Future fu = makeFuture(42).unit(); + fu.value(); + EXPECT_TRUE(makeFuture(eggs).unit().hasException()); +} + +TEST(Future, voidFutureToUnit) { + Future fu = makeFuture().unit(); + fu.value(); + EXPECT_TRUE(makeFuture(eggs).unit().hasException()); +} + +TEST(Future, unitFutureToUnitIdentity) { + Future fu = makeFuture(Unit{}).unit(); + fu.value(); + EXPECT_TRUE(makeFuture(eggs).unit().hasException()); +} + +TEST(Future, toUnitWhileInProgress) { + Promise p; + Future fu = p.getFuture().unit(); + EXPECT_FALSE(fu.isReady()); + p.setValue(42); + EXPECT_TRUE(fu.isReady()); +} + +TEST(Future, makeFutureWithUnit) { + int count = 0; + Future fu = makeFutureWith([&] { count++; }); + EXPECT_EQ(1, count); +} + +namespace { +Future onErrorHelperEggs(const eggs_t&) { + return makeFuture(10); +} +Future onErrorHelperGeneric(const std::exception&) { + return makeFuture(20); +} +} + TEST(Future, onError) { bool theFlag = false; auto flag = [&]{ theFlag = true; }; @@ -58,159 +102,208 @@ TEST(Future, onError) { // By reference { - auto f = makeFuture() - .then([] { throw eggs; }) - .onError([&] (eggs_t& e) { flag(); }); + auto f = makeFuture().then([] { + throw eggs; + }).onError([&](eggs_t& /* e */) { flag(); }); EXPECT_FLAG(); EXPECT_NO_THROW(f.value()); } { auto f = makeFuture() - .then([] { throw eggs; }) - .onError([&] (eggs_t& e) { flag(); return makeFuture(); }); + .then([] { throw eggs; }) + .onError([&](eggs_t& /* e */) { + flag(); + return makeFuture(); + }); EXPECT_FLAG(); EXPECT_NO_THROW(f.value()); } // By value { - auto f = makeFuture() - .then([] { throw eggs; }) - .onError([&] (eggs_t e) { flag(); }); + auto f = makeFuture().then([] { + throw eggs; + }).onError([&](eggs_t /* e */) { flag(); }); EXPECT_FLAG(); EXPECT_NO_THROW(f.value()); } { auto f = makeFuture() - .then([] { throw eggs; }) - .onError([&] (eggs_t e) { flag(); return makeFuture(); }); + .then([] { throw eggs; }) + .onError([&](eggs_t /* e */) { + flag(); + return makeFuture(); + }); EXPECT_FLAG(); EXPECT_NO_THROW(f.value()); } // Polymorphic { - auto f = makeFuture() - .then([] { throw eggs; }) - .onError([&] (std::exception& e) { flag(); }); + auto f = makeFuture().then([] { + throw eggs; + }).onError([&](std::exception& /* e */) { flag(); }); EXPECT_FLAG(); EXPECT_NO_THROW(f.value()); } { auto f = makeFuture() - .then([] { throw eggs; }) - .onError([&] (std::exception& e) { flag(); return makeFuture(); }); + .then([] { throw eggs; }) + .onError([&](std::exception& /* e */) { + flag(); + return makeFuture(); + }); EXPECT_FLAG(); EXPECT_NO_THROW(f.value()); } // Non-exceptions { - auto f = makeFuture() - .then([] { throw -1; }) - .onError([&] (int e) { flag(); }); + auto f = makeFuture().then([] { + throw - 1; + }).onError([&](int /* e */) { flag(); }); EXPECT_FLAG(); EXPECT_NO_THROW(f.value()); } { auto f = makeFuture() - .then([] { throw -1; }) - .onError([&] (int e) { flag(); return makeFuture(); }); + .then([] { throw - 1; }) + .onError([&](int /* e */) { + flag(); + return makeFuture(); + }); EXPECT_FLAG(); EXPECT_NO_THROW(f.value()); } // Mutable lambda { - auto f = makeFuture() - .then([] { throw eggs; }) - .onError([&] (eggs_t& e) mutable { flag(); }); + auto f = makeFuture().then([] { + throw eggs; + }).onError([&](eggs_t& /* e */) mutable { flag(); }); EXPECT_FLAG(); EXPECT_NO_THROW(f.value()); } { auto f = makeFuture() - .then([] { throw eggs; }) - .onError([&] (eggs_t& e) mutable { flag(); return makeFuture(); }); + .then([] { throw eggs; }) + .onError([&](eggs_t& /* e */) mutable { + flag(); + return makeFuture(); + }); EXPECT_FLAG(); EXPECT_NO_THROW(f.value()); } + // Function pointer + { + auto f = makeFuture() + .then([]() -> int { throw eggs; }) + .onError(onErrorHelperEggs) + .onError(onErrorHelperGeneric); + EXPECT_EQ(10, f.value()); + } + { + auto f = makeFuture() + .then([]() -> int { throw std::runtime_error("test"); }) + .onError(onErrorHelperEggs) + .onError(onErrorHelperGeneric); + EXPECT_EQ(20, f.value()); + } + { + auto f = makeFuture() + .then([]() -> int { throw std::runtime_error("test"); }) + .onError(onErrorHelperEggs); + EXPECT_THROW(f.value(), std::runtime_error); + } + // No throw { auto f = makeFuture() - .then([] { return 42; }) - .onError([&] (eggs_t& e) { flag(); return -1; }); + .then([] { return 42; }) + .onError([&](eggs_t& /* e */) { + flag(); + return -1; + }); EXPECT_NO_FLAG(); EXPECT_EQ(42, f.value()); } { auto f = makeFuture() - .then([] { return 42; }) - .onError([&] (eggs_t& e) { flag(); return makeFuture(-1); }); + .then([] { return 42; }) + .onError([&](eggs_t& /* e */) { + flag(); + return makeFuture(-1); + }); EXPECT_NO_FLAG(); EXPECT_EQ(42, f.value()); } // Catch different exception { - auto f = makeFuture() - .then([] { throw eggs; }) - .onError([&] (std::runtime_error& e) { flag(); }); + auto f = makeFuture().then([] { + throw eggs; + }).onError([&](std::runtime_error& /* e */) { flag(); }); EXPECT_NO_FLAG(); EXPECT_THROW(f.value(), eggs_t); } { auto f = makeFuture() - .then([] { throw eggs; }) - .onError([&] (std::runtime_error& e) { flag(); return makeFuture(); }); + .then([] { throw eggs; }) + .onError([&](std::runtime_error& /* e */) { + flag(); + return makeFuture(); + }); EXPECT_NO_FLAG(); EXPECT_THROW(f.value(), eggs_t); } // Returned value propagates { - auto f = makeFuture() - .then([] { throw eggs; return 0; }) - .onError([&] (eggs_t& e) { return 42; }); + auto f = makeFuture().then([]() -> int { + throw eggs; + }).onError([&](eggs_t& /* e */) { return 42; }); EXPECT_EQ(42, f.value()); } // Returned future propagates { - auto f = makeFuture() - .then([] { throw eggs; return 0; }) - .onError([&] (eggs_t& e) { return makeFuture(42); }); + auto f = makeFuture().then([]() -> int { + throw eggs; + }).onError([&](eggs_t& /* e */) { return makeFuture(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(-1); }); + .then([]() -> int { throw eggs; }) + .onError([&] (eggs_t& e) -> Future { throw e; }); EXPECT_THROW(f.value(), eggs_t); } // exception_wrapper, return Future { auto f = makeFuture() - .then([] { throw eggs; }) - .onError([&] (exception_wrapper e) { flag(); return makeFuture(); }); + .then([] { throw eggs; }) + .onError([&](exception_wrapper /* e */) { + flag(); + return makeFuture(); + }); EXPECT_FLAG(); EXPECT_NO_THROW(f.value()); } @@ -218,12 +311,13 @@ TEST(Future, onError) { // exception_wrapper, return Future but throw { auto f = makeFuture() - .then([]{ throw eggs; return 0; }) - .onError([&] (exception_wrapper e) { - flag(); - throw eggs; - return makeFuture(-1); - }); + .then([]() -> int { + throw eggs; + }) + .onError([&](exception_wrapper /* e */) -> Future { + flag(); + throw eggs; + }); EXPECT_FLAG(); EXPECT_THROW(f.value(), eggs_t); } @@ -231,11 +325,13 @@ TEST(Future, onError) { // exception_wrapper, return T { auto f = makeFuture() - .then([]{ throw eggs; return 0; }) - .onError([&] (exception_wrapper e) { - flag(); - return -1; - }); + .then([]() -> int { + throw eggs; + }) + .onError([&](exception_wrapper /* e */) { + flag(); + return -1; + }); EXPECT_FLAG(); EXPECT_EQ(-1, f.value()); } @@ -243,12 +339,13 @@ TEST(Future, onError) { // exception_wrapper, return T but throw { auto f = makeFuture() - .then([]{ throw eggs; return 0; }) - .onError([&] (exception_wrapper e) { - flag(); - throw eggs; - return -1; - }); + .then([]() -> int { + throw eggs; + }) + .onError([&](exception_wrapper /* e */) -> int { + flag(); + throw eggs; + }); EXPECT_FLAG(); EXPECT_THROW(f.value(), eggs_t); } @@ -256,11 +353,11 @@ TEST(Future, onError) { // const exception_wrapper& { auto f = makeFuture() - .then([] { throw eggs; }) - .onError([&] (const exception_wrapper& e) { - flag(); - return makeFuture(); - }); + .then([] { throw eggs; }) + .onError([&](const exception_wrapper& /* e */) { + flag(); + return makeFuture(); + }); EXPECT_FLAG(); EXPECT_NO_THROW(f.value()); } @@ -324,7 +421,7 @@ TEST(Future, thenTry) { EXPECT_TRUE(flag); flag = false; Promise p; - auto f = p.getFuture().then([&](Try&& t) { flag = true; }); + auto f = p.getFuture().then([&](Try&& /* t */) { flag = true; }); EXPECT_FALSE(flag); EXPECT_FALSE(f.isReady()); p.setValue(); @@ -350,7 +447,7 @@ TEST(Future, thenValue) { }); EXPECT_TRUE(flag); flag = false; - auto f = makeFuture(eggs).then([&](int i){}); + auto f = makeFuture(eggs).then([&](int /* i */) {}); EXPECT_THROW(f.value(), eggs_t); f = makeFuture(eggs).then([&]{}); @@ -364,9 +461,9 @@ TEST(Future, thenValueFuture) { .then([&](Try&& t) { flag = true; EXPECT_EQ(42, t.value()); }); EXPECT_TRUE(flag); flag = false; - makeFuture() - .then([]{ return makeFuture(); }) - .then([&](Try&& t) { flag = true; }); + makeFuture().then([] { + return makeFuture(); + }).then([&](Try&& /* t */) { flag = true; }); EXPECT_TRUE(flag); flag = false; } @@ -501,30 +598,73 @@ TEST(Future, makeFuture) { EXPECT_TYPE(makeFutureWith(fun), Future); EXPECT_EQ(42, makeFutureWith(fun).value()); + auto funf = [] { return makeFuture(43); }; + EXPECT_TYPE(makeFutureWith(funf), Future); + EXPECT_EQ(43, makeFutureWith(funf).value()); + auto failfun = []() -> int { throw eggs; }; EXPECT_TYPE(makeFutureWith(failfun), Future); + EXPECT_NO_THROW(makeFutureWith(failfun)); EXPECT_THROW(makeFutureWith(failfun).value(), eggs_t); + auto failfunf = []() -> Future { throw eggs; }; + EXPECT_TYPE(makeFutureWith(failfunf), Future); + EXPECT_NO_THROW(makeFutureWith(failfunf)); + EXPECT_THROW(makeFutureWith(failfunf).value(), eggs_t); + EXPECT_TYPE(makeFuture(), Future); } TEST(Future, finish) { auto x = std::make_shared(0); - { - Promise p; - auto f = p.getFuture().then([x](Try&& t) { *x = t.value(); }); - // The callback hasn't executed - EXPECT_EQ(0, *x); + Promise p; + auto f = p.getFuture().then([x](Try&& t) { *x = t.value(); }); - // The callback has a reference to x - EXPECT_EQ(2, x.use_count()); + // The callback hasn't executed + EXPECT_EQ(0, *x); - p.setValue(42); + // 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(Future, finishBigLambda) { + auto x = std::make_shared(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)> bulk_data = {{0}}; + + // suppress gcc warning about bulk_data not being used + EXPECT_EQ(bulk_data[0], 0); + + Promise p; + auto f = p.getFuture().then([x, bulk_data](Try&& t) { + (void)bulk_data; + *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 executed - EXPECT_EQ(42, *x); - } // the callback has been destructed // and has released its reference to x EXPECT_EQ(1, x.use_count()); @@ -616,8 +756,8 @@ TEST(Future, detachRace) { // 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>(); - auto f = folly::make_unique>(p->getFuture()); + auto p = std::make_unique>(); + auto f = std::make_unique>(p->getFuture()); folly::Baton<> baton; std::thread t1([&]{ baton.post(); @@ -635,15 +775,13 @@ TEST(Future, CircularDependencySharedPtrSelfReset) { Promise promise; auto ptr = std::make_shared>(promise.getFuture()); - ptr->then( - [ptr] (folly::Try&& uid) mutable { - EXPECT_EQ(1, ptr.use_count()); + ptr->then([ptr](folly::Try&& /* uid */) mutable { + EXPECT_EQ(1, ptr.use_count()); - // Leaving no references to ourselves. - ptr.reset(); - EXPECT_EQ(0, ptr.use_count()); - } - ); + // Leaving no references to ourselves. + ptr.reset(); + EXPECT_EQ(0, ptr.use_count()); + }); EXPECT_EQ(2, ptr.use_count()); @@ -691,7 +829,9 @@ TEST(Future, RequestContext) { if (throwsOnAdd_) { throw std::exception(); } v_.emplace_back(std::move(f)); } - void addWithPriority(Func f, int8_t prio) override { add(std::move(f)); } + void addWithPriority(Func f, int8_t /* prio */) override { + add(std::move(f)); + } uint8_t getNumPriorities() const override { return numPriorities_; } void setHandlesPriorities() { numPriorities_ = 2; } @@ -707,30 +847,32 @@ TEST(Future, RequestContext) { bool value; }; + Promise p1, p2; NewThreadExecutor e; - RequestContext::create(); - RequestContext::get()->setContextData("key", - folly::make_unique(true)); - auto checker = [](int lineno) { - return [lineno](Try&& t) { - auto d = static_cast( - RequestContext::get()->getContextData("key")); - EXPECT_TRUE(d && d->value) << "on line " << lineno; + { + folly::RequestContextScopeGuard rctx; + RequestContext::get()->setContextData( + "key", std::make_unique(true)); + auto checker = [](int lineno) { + return [lineno](Try&& /* t */) { + auto d = static_cast( + RequestContext::get()->getContextData("key")); + EXPECT_TRUE(d && d->value) << "on line " << lineno; + }; }; - }; - makeFuture(1).via(&e).then(checker(__LINE__)); + makeFuture(1).via(&e).then(checker(__LINE__)); - e.setHandlesPriorities(); - makeFuture(2).via(&e).then(checker(__LINE__)); + e.setHandlesPriorities(); + makeFuture(2).via(&e).then(checker(__LINE__)); - Promise p1, p2; - p1.getFuture().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); } @@ -738,3 +880,49 @@ TEST(Future, RequestContext) { 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(100).then(foo).value()); + EXPECT_EQ(203, makeFuture(200).then(cfoo).value()); + EXPECT_EQ(303, makeFuture(300).then(Foo()).value()); +} + +TEST(Future, invokeCallbackReturningFutureAsRvalue) { + struct Foo { + Future operator()(int x) & { + return x + 1; + } + Future operator()(int x) const& { + return x + 2; + } + Future 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(100).then(foo).value()); + EXPECT_EQ(203, makeFuture(200).then(cfoo).value()); + EXPECT_EQ(303, makeFuture(300).then(Foo()).value()); +}