From 5180b66230ee290a22bdba66f81e9f33e0216dd9 Mon Sep 17 00:00:00 2001 From: Hannes Roth Date: Fri, 5 Jun 2015 08:50:18 -0700 Subject: [PATCH] (Wangle) Clean up tests Summary: This always bothered me. Now: 1) Separate tests for each feature 2) `XTest.cpp` contains `TEST(X, lowerCaseCamelCase)` Also cleaned up some header files and using statements. Shamelessly trying to increase the number of lines that blame to me. Test Plan: Run all the tests. `git grep 'TEST' -- 'folly/futures/test/*Test.cpp' | cut -d ',' -f 1 | cut -d '/' -f 4 | sort -u` `diff <(git ls-files -- 'folly/futures/test/*Test.cpp' | cut -d '/' -f 4 | sort) <(git grep 'Test.cpp' -- folly/futures/TARGETS | cut -d '/' -f 4 | cut -d "'" -f 1 | sort)` Reviewed By: jsedgwick@fb.com Subscribers: trunkagent, folly-diffs@, jsedgwick, yfeldblum, chalfant FB internal diff: D2127359 Tasks: 6019442 Signature: t1:2127359:1433459532:54a91ae83d7fb2d0b3f3769f673fefea20f35435 --- folly/futures/test/Benchmark.cpp | 11 +- folly/futures/test/CollectTest.cpp | 450 ++++++ folly/futures/test/ContextTest.cpp | 60 + folly/futures/test/{main.cpp => CoreTest.cpp} | 15 +- folly/futures/test/EnsureTest.cpp | 33 + folly/futures/test/ExecutorTest.cpp | 27 +- folly/futures/test/{FSM.cpp => FSMTest.cpp} | 1 + folly/futures/test/FilterTest.cpp | 37 + folly/futures/test/FutureTest.cpp | 1373 +---------------- ...lientCompile.cpp => HeaderCompileTest.cpp} | 7 +- .../{Interrupts.cpp => InterruptTest.cpp} | 13 +- folly/futures/test/MapTest.cpp | 47 + .../test/{SugarTest.cpp => PollTest.cpp} | 22 +- folly/futures/test/PromiseTest.cpp | 128 ++ folly/futures/test/ReduceTest.cpp | 174 +++ folly/futures/test/SharedPromiseTest.cpp | 22 +- .../test/{Thens.cpp => ThenCompileTest.cpp} | 10 +- .../test/{Thens.h => ThenCompileTest.h} | 14 +- folly/futures/test/ThenTest.cpp | 27 +- folly/futures/test/TimekeeperTest.cpp | 7 +- folly/futures/test/{Try.cpp => TryTest.cpp} | 20 + folly/futures/test/UnitTest.cpp | 11 +- folly/futures/test/UnwrapTest.cpp | 51 + folly/futures/test/ViaTest.cpp | 141 +- folly/futures/test/WaitTest.cpp | 197 +++ folly/futures/test/WillEqualTest.cpp | 106 ++ folly/futures/test/WindowTest.cpp | 81 + .../test/{thens.rb => then_compile_test.rb} | 12 +- 28 files changed, 1664 insertions(+), 1433 deletions(-) create mode 100644 folly/futures/test/CollectTest.cpp create mode 100644 folly/futures/test/ContextTest.cpp rename folly/futures/test/{main.cpp => CoreTest.cpp} (70%) create mode 100644 folly/futures/test/EnsureTest.cpp rename folly/futures/test/{FSM.cpp => FSMTest.cpp} (99%) create mode 100644 folly/futures/test/FilterTest.cpp rename folly/futures/test/{ClientCompile.cpp => HeaderCompileTest.cpp} (91%) rename folly/futures/test/{Interrupts.cpp => InterruptTest.cpp} (87%) create mode 100644 folly/futures/test/MapTest.cpp rename folly/futures/test/{SugarTest.cpp => PollTest.cpp} (67%) create mode 100644 folly/futures/test/PromiseTest.cpp create mode 100644 folly/futures/test/ReduceTest.cpp rename folly/futures/test/{Thens.cpp => ThenCompileTest.cpp} (97%) rename folly/futures/test/{Thens.h => ThenCompileTest.h} (95%) rename folly/futures/test/{Try.cpp => TryTest.cpp} (88%) create mode 100644 folly/futures/test/UnwrapTest.cpp create mode 100644 folly/futures/test/WaitTest.cpp create mode 100644 folly/futures/test/WillEqualTest.cpp create mode 100644 folly/futures/test/WindowTest.cpp rename folly/futures/test/{thens.rb => then_compile_test.rb} (83%) diff --git a/folly/futures/test/Benchmark.cpp b/folly/futures/test/Benchmark.cpp index fbf2538f..54771b1e 100644 --- a/folly/futures/test/Benchmark.cpp +++ b/folly/futures/test/Benchmark.cpp @@ -15,15 +15,16 @@ */ #include + #include #include #include #include + #include #include using namespace folly; -using namespace std; namespace { @@ -89,8 +90,8 @@ BENCHMARK_RELATIVE(hundredThens) { BENCHMARK_DRAW_LINE() BENCHMARK(no_contention) { - vector> promises(10000); - vector> futures; + std::vector> promises(10000); + std::vector> futures; std::thread producer, consumer; BENCHMARK_SUSPEND { @@ -118,8 +119,8 @@ BENCHMARK(no_contention) { } BENCHMARK_RELATIVE(contention) { - vector> promises(10000); - vector> futures; + std::vector> promises(10000); + std::vector> futures; std::thread producer, consumer; sem_t sem; sem_init(&sem, 0, 0); diff --git a/folly/futures/test/CollectTest.cpp b/folly/futures/test/CollectTest.cpp new file mode 100644 index 00000000..ea4b3bf8 --- /dev/null +++ b/folly/futures/test/CollectTest.cpp @@ -0,0 +1,450 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +using namespace folly; + +typedef FutureException eggs_t; +static eggs_t eggs("eggs"); + +auto rng = std::mt19937(folly::randomNumberSeed()); + +TEST(Collect, collectAll) { + // returns a vector variant + { + std::vector> promises(10); + std::vector> futures; + + for (auto& p : promises) + futures.push_back(p.getFuture()); + + auto allf = collectAll(futures); + + std::shuffle(promises.begin(), promises.end(), rng); + for (auto& p : promises) { + EXPECT_FALSE(allf.isReady()); + p.setValue(42); + } + + EXPECT_TRUE(allf.isReady()); + auto& results = allf.value(); + for (auto& t : results) { + EXPECT_EQ(42, t.value()); + } + } + + // check error semantics + { + std::vector> promises(4); + std::vector> futures; + + for (auto& p : promises) + futures.push_back(p.getFuture()); + + auto allf = collectAll(futures); + + + promises[0].setValue(42); + promises[1].setException(eggs); + + EXPECT_FALSE(allf.isReady()); + + promises[2].setValue(42); + + EXPECT_FALSE(allf.isReady()); + + promises[3].setException(eggs); + + EXPECT_TRUE(allf.isReady()); + EXPECT_FALSE(allf.getTry().hasException()); + + auto& results = allf.value(); + EXPECT_EQ(42, results[0].value()); + EXPECT_TRUE(results[1].hasException()); + EXPECT_EQ(42, results[2].value()); + EXPECT_TRUE(results[3].hasException()); + } + + // check that futures are ready in then() + { + std::vector> promises(10); + std::vector> futures; + + for (auto& p : promises) + futures.push_back(p.getFuture()); + + auto allf = collectAll(futures) + .then([](Try>>&& ts) { + for (auto& f : ts.value()) + f.value(); + }); + + std::shuffle(promises.begin(), promises.end(), rng); + for (auto& p : promises) + p.setValue(); + EXPECT_TRUE(allf.isReady()); + } +} + +TEST(Collect, collect) { + // success case + { + std::vector> promises(10); + std::vector> futures; + + for (auto& p : promises) + futures.push_back(p.getFuture()); + + auto allf = collect(futures); + + std::shuffle(promises.begin(), promises.end(), rng); + 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 + { + std::vector> promises(10); + std::vector> futures; + + for (auto& p : promises) + futures.push_back(p.getFuture()); + + auto allf = collect(futures); + + std::shuffle(promises.begin(), promises.end(), rng); + 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 + { + std::vector> promises(10); + std::vector> futures; + + for (auto& p : promises) + futures.push_back(p.getFuture()); + + auto allf = collect(futures); + + std::shuffle(promises.begin(), promises.end(), rng); + for (auto& p : promises) { + EXPECT_FALSE(allf.isReady()); + p.setValue(); + } + + EXPECT_TRUE(allf.isReady()); + } + + // void futures failure case + { + std::vector> promises(10); + std::vector> futures; + + for (auto& p : promises) + futures.push_back(p.getFuture()); + + auto allf = collect(futures); + + std::shuffle(promises.begin(), promises.end(), rng); + 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); + } + + // move only compiles + { + std::vector>> promises(10); + std::vector>> futures; + + for (auto& p : promises) + futures.push_back(p.getFuture()); + + collect(futures); + } + +} + +struct NotDefaultConstructible { + NotDefaultConstructible() = delete; + explicit NotDefaultConstructible(int arg) : i(arg) {} + int i; +}; + +// We have a specialized implementation for non-default-constructible objects +// Ensure that it works and preserves order +TEST(Collect, collectNotDefaultConstructible) { + std::vector> promises(10); + std::vector> futures; + std::vector indices(10); + std::iota(indices.begin(), indices.end(), 0); + std::shuffle(indices.begin(), indices.end(), rng); + + for (auto& p : promises) + futures.push_back(p.getFuture()); + + auto allf = collect(futures); + + for (auto i : indices) { + EXPECT_FALSE(allf.isReady()); + promises[i].setValue(NotDefaultConstructible(i)); + } + + EXPECT_TRUE(allf.isReady()); + int i = 0; + for (auto val : allf.value()) { + EXPECT_EQ(i, val.i); + i++; + } +} + +TEST(Collect, collectAny) { + { + std::vector> promises(10); + std::vector> futures; + + for (auto& p : promises) + futures.push_back(p.getFuture()); + + for (auto& f : futures) { + EXPECT_FALSE(f.isReady()); + } + + auto anyf = collectAny(futures); + + /* futures were moved in, so these are invalid now */ + EXPECT_FALSE(anyf.isReady()); + + promises[7].setValue(42); + EXPECT_TRUE(anyf.isReady()); + auto& idx_fut = anyf.value(); + + auto i = idx_fut.first; + EXPECT_EQ(7, i); + + auto& f = idx_fut.second; + EXPECT_EQ(42, f.value()); + } + + // error + { + std::vector> promises(10); + std::vector> futures; + + for (auto& p : promises) + futures.push_back(p.getFuture()); + + for (auto& f : futures) { + EXPECT_FALSE(f.isReady()); + } + + auto anyf = collectAny(futures); + + EXPECT_FALSE(anyf.isReady()); + + promises[3].setException(eggs); + EXPECT_TRUE(anyf.isReady()); + EXPECT_TRUE(anyf.value().second.hasException()); + } + + // then() + { + std::vector> promises(10); + std::vector> futures; + + for (auto& p : promises) + futures.push_back(p.getFuture()); + + auto anyf = collectAny(futures) + .then([](std::pair> p) { + EXPECT_EQ(42, p.second.value()); + }); + + promises[3].setValue(42); + EXPECT_TRUE(anyf.isReady()); + } +} + + +TEST(Collect, alreadyCompleted) { + { + std::vector> fs; + for (int i = 0; i < 10; i++) + fs.push_back(makeFuture()); + + collectAll(fs) + .then([&](std::vector> ts) { + EXPECT_EQ(fs.size(), ts.size()); + }); + } + { + std::vector> fs; + for (int i = 0; i < 10; i++) + fs.push_back(makeFuture(i)); + + collectAny(fs) + .then([&](std::pair> p) { + EXPECT_EQ(p.first, p.second.value()); + }); + } +} + +TEST(Collect, collectN) { + std::vector> promises(10); + std::vector> futures; + + for (auto& p : promises) + futures.push_back(p.getFuture()); + + bool flag = false; + size_t n = 3; + collectN(futures, n) + .then([&](std::vector>> v) { + flag = true; + EXPECT_EQ(n, v.size()); + for (auto& tt : v) + EXPECT_TRUE(tt.second.hasValue()); + }); + + promises[0].setValue(); + EXPECT_FALSE(flag); + promises[1].setValue(); + EXPECT_FALSE(flag); + promises[2].setValue(); + EXPECT_TRUE(flag); +} + +/// Ensure that we can compile collectAll/Any with folly::small_vector +TEST(Collect, smallVector) { + static_assert(!FOLLY_IS_TRIVIALLY_COPYABLE(Future), + "Futures should not be trivially copyable"); + static_assert(!FOLLY_IS_TRIVIALLY_COPYABLE(Future), + "Futures should not be trivially copyable"); + + { + folly::small_vector> futures; + + for (int i = 0; i < 10; i++) + futures.push_back(makeFuture()); + + auto anyf = collectAny(futures); + } + { + folly::small_vector> futures; + + for (int i = 0; i < 10; i++) + futures.push_back(makeFuture()); + + auto allf = collectAll(futures); + } +} + +TEST(Collect, collectAllVariadic) { + Promise pb; + Promise pi; + Future fb = pb.getFuture(); + Future fi = pi.getFuture(); + bool flag = false; + collectAll(std::move(fb), std::move(fi)) + .then([&](std::tuple, Try> tup) { + flag = true; + EXPECT_TRUE(std::get<0>(tup).hasValue()); + EXPECT_EQ(std::get<0>(tup).value(), true); + EXPECT_TRUE(std::get<1>(tup).hasValue()); + EXPECT_EQ(std::get<1>(tup).value(), 42); + }); + pb.setValue(true); + EXPECT_FALSE(flag); + pi.setValue(42); + EXPECT_TRUE(flag); +} + +TEST(Collect, collectAllVariadicReferences) { + Promise pb; + Promise pi; + Future fb = pb.getFuture(); + Future fi = pi.getFuture(); + bool flag = false; + collectAll(fb, fi) + .then([&](std::tuple, Try> tup) { + flag = true; + EXPECT_TRUE(std::get<0>(tup).hasValue()); + EXPECT_EQ(std::get<0>(tup).value(), true); + EXPECT_TRUE(std::get<1>(tup).hasValue()); + EXPECT_EQ(std::get<1>(tup).value(), 42); + }); + pb.setValue(true); + EXPECT_FALSE(flag); + pi.setValue(42); + EXPECT_TRUE(flag); +} + +TEST(Collect, collectAllNone) { + std::vector> fs; + auto f = collectAll(fs); + EXPECT_TRUE(f.isReady()); +} diff --git a/folly/futures/test/ContextTest.cpp b/folly/futures/test/ContextTest.cpp new file mode 100644 index 00000000..dd29b155 --- /dev/null +++ b/folly/futures/test/ContextTest.cpp @@ -0,0 +1,60 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +using namespace folly; + +class TestData : public RequestData { + public: + explicit TestData(int data) : data_(data) {} + virtual ~TestData() {} + int data_; +}; + +TEST(Context, basic) { + + // Start a new context + RequestContext::create(); + + EXPECT_EQ(nullptr, RequestContext::get()->getContextData("test")); + + // Set some test data + RequestContext::get()->setContextData( + "test", + std::unique_ptr(new TestData(10))); + + // Start a future + Promise p; + auto future = p.getFuture().then([&]{ + // Check that the context followed the future + EXPECT_TRUE(RequestContext::get() != nullptr); + auto a = dynamic_cast( + RequestContext::get()->getContextData("test")); + auto data = a->data_; + EXPECT_EQ(10, data); + }); + + // Clear the context + RequestContext::setContext(nullptr); + + EXPECT_EQ(nullptr, RequestContext::get()->getContextData("test")); + + // Fulfill the promise + p.setValue(); +} diff --git a/folly/futures/test/main.cpp b/folly/futures/test/CoreTest.cpp similarity index 70% rename from folly/futures/test/main.cpp rename to folly/futures/test/CoreTest.cpp index a596ae66..7e2ad647 100644 --- a/folly/futures/test/main.cpp +++ b/folly/futures/test/CoreTest.cpp @@ -15,11 +15,14 @@ */ #include -#include -#include -int main(int argc, char** argv) { - ::testing::InitGoogleTest(&argc, argv); - ::gflags::ParseCommandLineFlags(&argc, &argv, true); - return RUN_ALL_TESTS(); +#include +#include + +using namespace folly; + +TEST(Core, size) { + // If this number goes down, it's fine! + // If it goes up, please seek professional advice ;-) + EXPECT_EQ(192, sizeof(detail::Core)); } diff --git a/folly/futures/test/EnsureTest.cpp b/folly/futures/test/EnsureTest.cpp new file mode 100644 index 00000000..b3fa6b0c --- /dev/null +++ b/folly/futures/test/EnsureTest.cpp @@ -0,0 +1,33 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +using namespace folly; + +TEST(Ensure, basic) { + 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); +} diff --git a/folly/futures/test/ExecutorTest.cpp b/folly/futures/test/ExecutorTest.cpp index e477f5cc..c7b06dd6 100644 --- a/folly/futures/test/ExecutorTest.cpp +++ b/folly/futures/test/ExecutorTest.cpp @@ -15,15 +15,14 @@ */ #include + +#include #include #include #include -#include #include using namespace folly; -using namespace std::chrono; -using namespace testing; TEST(ManualExecutor, runIsStable) { ManualExecutor x; @@ -37,7 +36,7 @@ TEST(ManualExecutor, runIsStable) { TEST(ManualExecutor, scheduleDur) { ManualExecutor x; size_t count = 0; - milliseconds dur {10}; + std::chrono::milliseconds dur {10}; x.schedule([&]{ count++; }, dur); EXPECT_EQ(count, 0); x.run(); @@ -56,38 +55,38 @@ TEST(ManualExecutor, clockStartsAt0) { TEST(ManualExecutor, scheduleAbs) { ManualExecutor x; size_t count = 0; - x.scheduleAt([&]{ count++; }, x.now() + milliseconds(10)); + x.scheduleAt([&]{ count++; }, x.now() + std::chrono::milliseconds(10)); EXPECT_EQ(count, 0); - x.advance(milliseconds(10)); + x.advance(std::chrono::milliseconds(10)); EXPECT_EQ(count, 1); } TEST(ManualExecutor, advanceTo) { ManualExecutor x; size_t count = 0; - x.scheduleAt([&]{ count++; }, steady_clock::now()); + x.scheduleAt([&]{ count++; }, std::chrono::steady_clock::now()); EXPECT_EQ(count, 0); - x.advanceTo(steady_clock::now()); + x.advanceTo(std::chrono::steady_clock::now()); EXPECT_EQ(count, 1); } TEST(ManualExecutor, advanceBack) { ManualExecutor x; size_t count = 0; - x.advance(microseconds(5)); - x.schedule([&]{ count++; }, microseconds(6)); + x.advance(std::chrono::microseconds(5)); + x.schedule([&]{ count++; }, std::chrono::microseconds(6)); EXPECT_EQ(count, 0); - x.advanceTo(x.now() - microseconds(1)); + x.advanceTo(x.now() - std::chrono::microseconds(1)); EXPECT_EQ(count, 0); } TEST(ManualExecutor, advanceNeg) { ManualExecutor x; size_t count = 0; - x.advance(microseconds(5)); - x.schedule([&]{ count++; }, microseconds(6)); + x.advance(std::chrono::microseconds(5)); + x.schedule([&]{ count++; }, std::chrono::microseconds(6)); EXPECT_EQ(count, 0); - x.advance(microseconds(-1)); + x.advance(std::chrono::microseconds(-1)); EXPECT_EQ(count, 0); } diff --git a/folly/futures/test/FSM.cpp b/folly/futures/test/FSMTest.cpp similarity index 99% rename from folly/futures/test/FSM.cpp rename to folly/futures/test/FSMTest.cpp index f5b871ab..ee28b45b 100644 --- a/folly/futures/test/FSM.cpp +++ b/folly/futures/test/FSMTest.cpp @@ -15,6 +15,7 @@ */ #include + #include using namespace folly::detail; diff --git a/folly/futures/test/FilterTest.cpp b/folly/futures/test/FilterTest.cpp new file mode 100644 index 00000000..5b370800 --- /dev/null +++ b/folly/futures/test/FilterTest.cpp @@ -0,0 +1,37 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +using namespace folly; + +TEST(Filter, alwaysTrye) { + EXPECT_EQ(42, makeFuture(42).filter([](int){ return true; }).get()); +} + +TEST(Filter, alwaysFalse) { + EXPECT_THROW(makeFuture(42).filter([](int){ return false; }).get(), + folly::PredicateDoesNotObtain); +} + +TEST(Filter, moveOnlyValue) { + EXPECT_EQ(42, + *makeFuture(folly::make_unique(42)) + .filter([](std::unique_ptr const&) { return true; }) + .get()); +} diff --git a/folly/futures/test/FutureTest.cpp b/folly/futures/test/FutureTest.cpp index 58619d69..5d171bb9 100644 --- a/folly/futures/test/FutureTest.cpp +++ b/folly/futures/test/FutureTest.cpp @@ -14,87 +14,30 @@ * limitations under the License. */ +#include + +#include +#include +#include +#include +#include + #include #include -#include -#include #include #include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include using namespace folly; -using std::pair; -using std::string; -using std::unique_ptr; -using std::vector; -using std::chrono::milliseconds; #define EXPECT_TYPE(x, T) \ EXPECT_TRUE((std::is_same::value)) -/// Simple executor that does work in another thread -class ThreadExecutor : public Executor { - folly::MPMCQueue funcs; - std::atomic done {false}; - std::thread worker; - folly::Baton<> baton; - - void work() { - baton.post(); - Func fn; - while (!done) { - while (!funcs.isEmpty()) { - funcs.blockingRead(fn); - fn(); - } - } - } - - public: - explicit ThreadExecutor(size_t n = 1024) - : funcs(n) { - worker = std::thread(std::bind(&ThreadExecutor::work, this)); - } - - ~ThreadExecutor() { - done = true; - funcs.write([]{}); - worker.join(); - } - - void add(Func fn) override { - funcs.blockingWrite(std::move(fn)); - } - - void waitForStartup() { - baton.wait(); - } -}; - typedef FutureException eggs_t; static eggs_t eggs("eggs"); -// Core - -TEST(Future, coreSize) { - // If this number goes down, it's fine! - // If it goes up, please seek professional advice ;-) - EXPECT_EQ(192, sizeof(detail::Core)); -} - // Future TEST(Future, onError) { @@ -323,26 +266,6 @@ TEST(Future, onError) { } -TEST(Future, try) { - class A { - public: - A(int x) : x_(x) {} - - int x() const { - return x_; - } - private: - int x_; - }; - - A a(5); - Try t_a(std::move(a)); - - Try t_void; - - EXPECT_EQ(5, t_a.value().x()); -} - TEST(Future, special) { EXPECT_FALSE(std::is_copy_constructible>::value); EXPECT_FALSE(std::is_copy_assignable>::value); @@ -351,20 +274,33 @@ TEST(Future, special) { } TEST(Future, then) { - auto f = makeFuture("0") - .then([](){ return makeFuture("1"); }) - .then([](Try&& t) { return makeFuture(t.value() + ";2"); }) - .then([](const Try&& t) { return makeFuture(t.value() + ";3"); }) - .then([](Try& t) { return makeFuture(t.value() + ";4"); }) - .then([](const Try& t) { return makeFuture(t.value() + ";5"); }) - .then([](Try t) { return makeFuture(t.value() + ";6"); }) - .then([](const Try t) { return makeFuture(t.value() + ";7"); }) - .then([](string&& s) { return makeFuture(s + ";8"); }) - .then([](const string&& s) { return makeFuture(s + ";9"); }) - .then([](string& s) { return makeFuture(s + ";10"); }) - .then([](const string& s) { return makeFuture(s + ";11"); }) - .then([](string s) { return makeFuture(s + ";12"); }) - .then([](const string s) { return makeFuture(s + ";13"); }) + auto f = makeFuture("0") + .then([](){ + return makeFuture("1"); }) + .then([](Try&& t) { + return makeFuture(t.value() + ";2"); }) + .then([](const Try&& t) { + return makeFuture(t.value() + ";3"); }) + .then([](Try& t) { + return makeFuture(t.value() + ";4"); }) + .then([](const Try& t) { + return makeFuture(t.value() + ";5"); }) + .then([](Try t) { + return makeFuture(t.value() + ";6"); }) + .then([](const Try t) { + return makeFuture(t.value() + ";7"); }) + .then([](std::string&& s) { + return makeFuture(s + ";8"); }) + .then([](const std::string&& s) { + return makeFuture(s + ";9"); }) + .then([](std::string& s) { + return makeFuture(s + ";10"); }) + .then([](const std::string& s) { + return makeFuture(s + ";11"); }) + .then([](std::string s) { + return makeFuture(s + ";12"); }) + .then([](const std::string s) { + return makeFuture(s + ";13"); }) ; EXPECT_EQ(f.value(), "1;2;3;4;5;6;7;8;9;10;11;12;13"); } @@ -433,21 +369,21 @@ TEST(Future, thenValueFuture) { EXPECT_TRUE(flag); flag = false; } -static string doWorkStatic(Try&& t) { +static std::string doWorkStatic(Try&& t) { return t.value() + ";static"; } TEST(Future, thenFunction) { struct Worker { - string doWork(Try&& t) { + std::string doWork(Try&& t) { return t.value() + ";class"; } - static string doWorkStatic(Try&& t) { + static std::string doWorkStatic(Try&& t) { return t.value() + ";class-static"; } } w; - auto f = makeFuture("start") + auto f = makeFuture("start") .then(doWorkStatic) .then(Worker::doWorkStatic) .then(&Worker::doWork, &w); @@ -455,21 +391,21 @@ TEST(Future, thenFunction) { EXPECT_EQ(f.value(), "start;static;class-static;class"); } -static Future doWorkStaticFuture(Try&& t) { +static Future doWorkStaticFuture(Try&& t) { return makeFuture(t.value() + ";static"); } TEST(Future, thenFunctionFuture) { struct Worker { - Future doWorkFuture(Try&& t) { + Future doWorkFuture(Try&& t) { return makeFuture(t.value() + ";class"); } - static Future doWorkStaticFuture(Try&& t) { + static Future doWorkStaticFuture(Try&& t) { return makeFuture(t.value() + ";class-static"); } } w; - auto f = makeFuture("start") + auto f = makeFuture("start") .then(doWorkStaticFuture) .then(Worker::doWorkStaticFuture) .then(&Worker::doWorkFuture, &w); @@ -487,17 +423,17 @@ TEST(Future, thenBind) { } TEST(Future, thenBindTry) { - auto l = [](Try&& t) { + auto l = [](Try&& t) { return makeFuture(t.value() + ";bind"); }; auto b = std::bind(l, std::placeholders::_1); - auto f = makeFuture("start").then(std::move(b)); + auto f = makeFuture("start").then(std::move(b)); EXPECT_EQ(f.value(), "start;bind"); } TEST(Future, value) { - auto f = makeFuture(unique_ptr(new int(42))); + auto f = makeFuture(std::unique_ptr(new int(42))); auto up = std::move(f.value()); EXPECT_EQ(42, *up); @@ -546,93 +482,6 @@ TEST(Future, makeFuture) { EXPECT_TYPE(makeFuture(), Future); } -// Promise - -TEST(Promise, special) { - EXPECT_FALSE(std::is_copy_constructible>::value); - EXPECT_FALSE(std::is_copy_assignable>::value); - EXPECT_TRUE(std::is_move_constructible>::value); - EXPECT_TRUE(std::is_move_assignable>::value); -} - -TEST(Promise, getFuture) { - Promise p; - Future f = p.getFuture(); - EXPECT_FALSE(f.isReady()); -} - -TEST(Promise, setValue) { - Promise fund; - auto ffund = fund.getFuture(); - fund.setValue(42); - EXPECT_EQ(42, ffund.value()); - - struct Foo { - string name; - int value; - }; - - Promise pod; - auto fpod = pod.getFuture(); - Foo f = {"the answer", 42}; - pod.setValue(f); - Foo f2 = fpod.value(); - EXPECT_EQ(f.name, f2.name); - EXPECT_EQ(f.value, f2.value); - - pod = Promise(); - fpod = pod.getFuture(); - pod.setValue(std::move(f2)); - Foo f3 = fpod.value(); - EXPECT_EQ(f.name, f3.name); - EXPECT_EQ(f.value, f3.value); - - Promise> mov; - auto fmov = mov.getFuture(); - mov.setValue(unique_ptr(new int(42))); - unique_ptr ptr = std::move(fmov.value()); - EXPECT_EQ(42, *ptr); - - Promise v; - auto fv = v.getFuture(); - v.setValue(); - EXPECT_TRUE(fv.isReady()); -} - -TEST(Promise, setException) { - { - Promise p; - auto f = p.getFuture(); - p.setException(eggs); - EXPECT_THROW(f.value(), eggs_t); - } - { - Promise p; - auto f = p.getFuture(); - try { - throw eggs; - } catch (...) { - p.setException(exception_wrapper(std::current_exception())); - } - EXPECT_THROW(f.value(), eggs_t); - } -} - -TEST(Promise, setWith) { - { - Promise p; - auto f = p.getFuture(); - p.setWith([] { return 42; }); - EXPECT_EQ(42, f.value()); - } - { - Promise p; - auto f = p.getFuture(); - p.setWith([]() -> int { throw eggs; }); - EXPECT_THROW(f.value(), eggs_t); - } -} - TEST(Future, finish) { auto x = std::make_shared(0); { @@ -690,489 +539,6 @@ TEST(Future, unwrap) { EXPECT_EQ(7, f.value()); } -TEST(Future, window) { - // int -> Future - auto fn = [](vector input, size_t window_size, size_t expect) { - auto res = reduce( - window( - input, - [](int i) { return makeFuture(i); }, - 2), - 0, - [](int sum, const Try& b) { - return sum + *b; - }).get(); - EXPECT_EQ(expect, res); - }; - { - // 2 in-flight at a time - vector input = {1, 2, 3}; - fn(input, 2, 6); - } - { - // 4 in-flight at a time - vector input = {1, 2, 3}; - fn(input, 4, 6); - } - { - // empty inpt - vector input; - fn(input, 1, 0); - } - { - // int -> Future - auto res = reduce( - window( - std::vector({1, 2, 3}), - [](int i) { return makeFuture(); }, - 2), - 0, - [](int sum, const Try& b) { - EXPECT_TRUE(b.hasValue()); - return sum + 1; - }).get(); - EXPECT_EQ(3, res); - } - { - // string -> return Future - auto res = reduce( - window( - std::vector{"1", "2", "3"}, - [](std::string s) { return makeFuture(folly::to(s)); }, - 2), - 0, - [](int sum, const Try& b) { - return sum + *b; - }).get(); - EXPECT_EQ(6, res); - } -} - -TEST(Future, collectAll) { - // returns a vector variant - { - vector> promises(10); - vector> futures; - - for (auto& p : promises) - futures.push_back(p.getFuture()); - - auto allf = collectAll(futures); - - random_shuffle(promises.begin(), promises.end()); - for (auto& p : promises) { - EXPECT_FALSE(allf.isReady()); - p.setValue(42); - } - - EXPECT_TRUE(allf.isReady()); - auto& results = allf.value(); - for (auto& t : results) { - EXPECT_EQ(42, t.value()); - } - } - - // check error semantics - { - vector> promises(4); - vector> futures; - - for (auto& p : promises) - futures.push_back(p.getFuture()); - - auto allf = collectAll(futures); - - - promises[0].setValue(42); - promises[1].setException(eggs); - - EXPECT_FALSE(allf.isReady()); - - promises[2].setValue(42); - - EXPECT_FALSE(allf.isReady()); - - promises[3].setException(eggs); - - EXPECT_TRUE(allf.isReady()); - EXPECT_FALSE(allf.getTry().hasException()); - - auto& results = allf.value(); - EXPECT_EQ(42, results[0].value()); - EXPECT_TRUE(results[1].hasException()); - EXPECT_EQ(42, results[2].value()); - EXPECT_TRUE(results[3].hasException()); - } - - // check that futures are ready in then() - { - vector> promises(10); - vector> futures; - - for (auto& p : promises) - futures.push_back(p.getFuture()); - - auto allf = collectAll(futures) - .then([](Try>>&& ts) { - for (auto& f : ts.value()) - f.value(); - }); - - random_shuffle(promises.begin(), promises.end()); - for (auto& p : promises) - p.setValue(); - EXPECT_TRUE(allf.isReady()); - } -} - -TEST(Future, collect) { - // success case - { - vector> promises(10); - vector> futures; - - for (auto& p : promises) - futures.push_back(p.getFuture()); - - auto allf = collect(futures); - - 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); - - 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); - - 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); - - 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); - } - - // move only compiles - { - vector>> promises(10); - vector>> futures; - - for (auto& p : promises) - futures.push_back(p.getFuture()); - - collect(futures); - } - -} - -struct NotDefaultConstructible { - NotDefaultConstructible() = delete; - NotDefaultConstructible(int arg) : i(arg) {} - int i; -}; - -// We have a specialized implementation for non-default-constructible objects -// Ensure that it works and preserves order -TEST(Future, collectNotDefaultConstructible) { - vector> promises(10); - vector> futures; - vector indices(10); - std::iota(indices.begin(), indices.end(), 0); - random_shuffle(indices.begin(), indices.end()); - - for (auto& p : promises) - futures.push_back(p.getFuture()); - - auto allf = collect(futures); - - for (auto i : indices) { - EXPECT_FALSE(allf.isReady()); - promises[i].setValue(NotDefaultConstructible(i)); - } - - EXPECT_TRUE(allf.isReady()); - int i = 0; - for (auto val : allf.value()) { - EXPECT_EQ(i, val.i); - i++; - } -} - -TEST(Future, collectAny) { - { - vector> promises(10); - vector> futures; - - for (auto& p : promises) - futures.push_back(p.getFuture()); - - for (auto& f : futures) { - EXPECT_FALSE(f.isReady()); - } - - auto anyf = collectAny(futures); - - /* futures were moved in, so these are invalid now */ - EXPECT_FALSE(anyf.isReady()); - - promises[7].setValue(42); - EXPECT_TRUE(anyf.isReady()); - auto& idx_fut = anyf.value(); - - auto i = idx_fut.first; - EXPECT_EQ(7, i); - - auto& f = idx_fut.second; - EXPECT_EQ(42, f.value()); - } - - // error - { - vector> promises(10); - vector> futures; - - for (auto& p : promises) - futures.push_back(p.getFuture()); - - for (auto& f : futures) { - EXPECT_FALSE(f.isReady()); - } - - auto anyf = collectAny(futures); - - EXPECT_FALSE(anyf.isReady()); - - promises[3].setException(eggs); - EXPECT_TRUE(anyf.isReady()); - EXPECT_TRUE(anyf.value().second.hasException()); - } - - // then() - { - vector> promises(10); - vector> futures; - - for (auto& p : promises) - futures.push_back(p.getFuture()); - - auto anyf = collectAny(futures) - .then([](pair> p) { - EXPECT_EQ(42, p.second.value()); - }); - - promises[3].setValue(42); - EXPECT_TRUE(anyf.isReady()); - } -} - - -TEST(when, already_completed) { - { - vector> fs; - for (int i = 0; i < 10; i++) - fs.push_back(makeFuture()); - - collectAll(fs) - .then([&](vector> ts) { - EXPECT_EQ(fs.size(), ts.size()); - }); - } - { - vector> fs; - for (int i = 0; i < 10; i++) - fs.push_back(makeFuture(i)); - - collectAny(fs) - .then([&](pair> p) { - EXPECT_EQ(p.first, p.second.value()); - }); - } -} - -TEST(when, collectN) { - vector> promises(10); - vector> futures; - - for (auto& p : promises) - futures.push_back(p.getFuture()); - - bool flag = false; - size_t n = 3; - collectN(futures, n) - .then([&](vector>> v) { - flag = true; - EXPECT_EQ(n, v.size()); - for (auto& tt : v) - EXPECT_TRUE(tt.second.hasValue()); - }); - - promises[0].setValue(); - EXPECT_FALSE(flag); - promises[1].setValue(); - EXPECT_FALSE(flag); - promises[2].setValue(); - EXPECT_TRUE(flag); -} - -/* Ensure that we can compile when_{all,any} with folly::small_vector */ -TEST(when, small_vector) { - - static_assert(!FOLLY_IS_TRIVIALLY_COPYABLE(Future), - "Futures should not be trivially copyable"); - static_assert(!FOLLY_IS_TRIVIALLY_COPYABLE(Future), - "Futures should not be trivially copyable"); - - using folly::small_vector; - { - small_vector> futures; - - for (int i = 0; i < 10; i++) - futures.push_back(makeFuture()); - - auto anyf = collectAny(futures); - } - - { - small_vector> futures; - - for (int i = 0; i < 10; i++) - futures.push_back(makeFuture()); - - auto allf = collectAll(futures); - } -} - -TEST(Future, collectAllVariadic) { - Promise pb; - Promise pi; - Future fb = pb.getFuture(); - Future fi = pi.getFuture(); - bool flag = false; - collectAll(std::move(fb), std::move(fi)) - .then([&](std::tuple, Try> tup) { - flag = true; - EXPECT_TRUE(std::get<0>(tup).hasValue()); - EXPECT_EQ(std::get<0>(tup).value(), true); - EXPECT_TRUE(std::get<1>(tup).hasValue()); - EXPECT_EQ(std::get<1>(tup).value(), 42); - }); - pb.setValue(true); - EXPECT_FALSE(flag); - pi.setValue(42); - EXPECT_TRUE(flag); -} - -TEST(Future, collectAllVariadicReferences) { - Promise pb; - Promise pi; - Future fb = pb.getFuture(); - Future fi = pi.getFuture(); - bool flag = false; - collectAll(fb, fi) - .then([&](std::tuple, Try> tup) { - flag = true; - EXPECT_TRUE(std::get<0>(tup).hasValue()); - EXPECT_EQ(std::get<0>(tup).value(), true); - EXPECT_TRUE(std::get<1>(tup).hasValue()); - EXPECT_EQ(std::get<1>(tup).value(), 42); - }); - pb.setValue(true); - EXPECT_FALSE(flag); - pi.setValue(42); - EXPECT_TRUE(flag); -} - -TEST(Future, collectAll_none) { - vector> fs; - auto f = collectAll(fs); - EXPECT_TRUE(f.isReady()); -} - TEST(Future, throwCaughtInImmediateThen) { // Neither of these should throw "Promise already satisfied" makeFuture().then( @@ -1201,260 +567,13 @@ TEST(Future, throwIfFailed) { }); } -TEST(Future, waitImmediate) { - makeFuture().wait(); - auto done = makeFuture(42).wait().value(); - EXPECT_EQ(42, done); - - vector v{1,2,3}; - auto done_v = makeFuture(v).wait().value(); - EXPECT_EQ(v.size(), done_v.size()); - EXPECT_EQ(v, done_v); - - vector> v_f; - v_f.push_back(makeFuture()); - v_f.push_back(makeFuture()); - auto done_v_f = collectAll(v_f).wait().value(); - EXPECT_EQ(2, done_v_f.size()); - - vector> v_fb; - v_fb.push_back(makeFuture(true)); - v_fb.push_back(makeFuture(false)); - auto fut = collectAll(v_fb); - auto done_v_fb = std::move(fut.wait().value()); - EXPECT_EQ(2, done_v_fb.size()); -} - -TEST(Future, wait) { - Promise p; - Future f = p.getFuture(); - std::atomic flag{false}; - std::atomic result{1}; - std::atomic id; - - std::thread t([&](Future&& tf){ - auto n = tf.then([&](Try && t) { - id = std::this_thread::get_id(); - return t.value(); - }); - flag = true; - result.store(n.wait().value()); - }, - std::move(f) - ); - while(!flag){} - EXPECT_EQ(result.load(), 1); - p.setValue(42); - t.join(); - // validate that the callback ended up executing in this thread, which - // is more to ensure that this test actually tests what it should - EXPECT_EQ(id, std::this_thread::get_id()); - EXPECT_EQ(result.load(), 42); -} - -struct MoveFlag { - MoveFlag() = default; - MoveFlag(const MoveFlag&) = delete; - MoveFlag(MoveFlag&& other) noexcept { - other.moved = true; - } - bool moved{false}; -}; - -TEST(Future, waitReplacesSelf) { - // wait - { - // lvalue - auto f1 = makeFuture(MoveFlag()); - f1.wait(); - EXPECT_FALSE(f1.value().moved); - - // rvalue - auto f2 = makeFuture(MoveFlag()).wait(); - EXPECT_FALSE(f2.value().moved); - } - - // wait(Duration) - { - // lvalue - auto f1 = makeFuture(MoveFlag()); - f1.wait(milliseconds(1)); - EXPECT_FALSE(f1.value().moved); - - // rvalue - auto f2 = makeFuture(MoveFlag()).wait(milliseconds(1)); - EXPECT_FALSE(f2.value().moved); - } - - // waitVia - { - folly::EventBase eb; - // lvalue - auto f1 = makeFuture(MoveFlag()); - f1.waitVia(&eb); - EXPECT_FALSE(f1.value().moved); - - // rvalue - auto f2 = makeFuture(MoveFlag()).waitVia(&eb); - EXPECT_FALSE(f2.value().moved); - } -} - -TEST(Future, waitWithDuration) { - { - Promise p; - Future f = p.getFuture(); - f.wait(milliseconds(1)); - EXPECT_FALSE(f.isReady()); - p.setValue(1); - EXPECT_TRUE(f.isReady()); - } - { - Promise p; - Future f = p.getFuture(); - p.setValue(1); - f.wait(milliseconds(1)); - EXPECT_TRUE(f.isReady()); - } - { - vector> v_fb; - v_fb.push_back(makeFuture(true)); - v_fb.push_back(makeFuture(false)); - auto f = collectAll(v_fb); - f.wait(milliseconds(1)); - EXPECT_TRUE(f.isReady()); - EXPECT_EQ(2, f.value().size()); - } - { - vector> v_fb; - Promise p1; - Promise p2; - v_fb.push_back(p1.getFuture()); - v_fb.push_back(p2.getFuture()); - auto f = collectAll(v_fb); - f.wait(milliseconds(1)); - EXPECT_FALSE(f.isReady()); - p1.setValue(true); - EXPECT_FALSE(f.isReady()); - p2.setValue(true); - EXPECT_TRUE(f.isReady()); - } - { - auto f = makeFuture().wait(milliseconds(1)); - EXPECT_TRUE(f.isReady()); - } - - { - Promise p; - auto start = std::chrono::steady_clock::now(); - auto f = p.getFuture().wait(milliseconds(100)); - auto elapsed = std::chrono::steady_clock::now() - start; - EXPECT_GE(elapsed, milliseconds(100)); - EXPECT_FALSE(f.isReady()); - p.setValue(); - EXPECT_TRUE(f.isReady()); - } - - { - // Try to trigger the race where the resultant Future is not yet complete - // even if we didn't hit the timeout, and make sure we deal with it properly - Promise p; - folly::Baton<> b; - auto t = std::thread([&]{ - b.post(); - /* sleep override */ std::this_thread::sleep_for(milliseconds(100)); - p.setValue(); - }); - b.wait(); - auto f = p.getFuture().wait(std::chrono::seconds(3600)); - EXPECT_TRUE(f.isReady()); - t.join(); - } -} - -class DummyDrivableExecutor : public DrivableExecutor { - public: - void add(Func f) override {} - void drive() override { ran = true; } - bool ran{false}; -}; - -TEST(Future, getVia) { - { - // non-void - ManualExecutor x; - auto f = via(&x).then([]{ return true; }); - EXPECT_TRUE(f.getVia(&x)); - } - - { - // void - ManualExecutor x; - auto f = via(&x).then(); - f.getVia(&x); - } - - { - DummyDrivableExecutor x; - auto f = makeFuture(true); - EXPECT_TRUE(f.getVia(&x)); - EXPECT_FALSE(x.ran); - } -} - -TEST(Future, waitVia) { - { - ManualExecutor x; - auto f = via(&x).then(); - EXPECT_FALSE(f.isReady()); - f.waitVia(&x); - EXPECT_TRUE(f.isReady()); - } - - { - // try rvalue as well - ManualExecutor x; - auto f = via(&x).then().waitVia(&x); - EXPECT_TRUE(f.isReady()); - } - - { - DummyDrivableExecutor x; - makeFuture(true).waitVia(&x); - EXPECT_FALSE(x.ran); - } -} - -TEST(Future, viaRaces) { - ManualExecutor x; - Promise p; - auto tid = std::this_thread::get_id(); - bool done = false; - - std::thread t1([&] { - p.getFuture() - .via(&x) - .then([&](Try&&) { EXPECT_EQ(tid, std::this_thread::get_id()); }) - .then([&](Try&&) { EXPECT_EQ(tid, std::this_thread::get_id()); }) - .then([&](Try&&) { done = true; }); - }); - - std::thread t2([&] { - p.setValue(); - }); - - while (!done) x.run(); - t1.join(); - t2.join(); -} - -TEST(Future, getFuture_after_setValue) { +TEST(Future, getFutureAfterSetValue) { Promise p; p.setValue(42); EXPECT_EQ(42, p.getFuture().value()); } -TEST(Future, getFuture_after_setException) { +TEST(Future, getFutureAfterSetException) { Promise p; p.setWith([]() -> void { throw std::logic_error("foo"); }); EXPECT_THROW(p.getFuture().value(), std::logic_error); @@ -1483,72 +602,6 @@ TEST(Future, detachRace) { t1.join(); } -class TestData : public RequestData { - public: - explicit TestData(int data) : data_(data) {} - virtual ~TestData() {} - int data_; -}; - -TEST(Future, context) { - - // Start a new context - RequestContext::create(); - - EXPECT_EQ(nullptr, RequestContext::get()->getContextData("test")); - - // Set some test data - RequestContext::get()->setContextData( - "test", - std::unique_ptr(new TestData(10))); - - // Start a future - Promise p; - auto future = p.getFuture().then([&]{ - // Check that the context followed the future - EXPECT_TRUE(RequestContext::get() != nullptr); - auto a = dynamic_cast( - RequestContext::get()->getContextData("test")); - auto data = a->data_; - EXPECT_EQ(10, data); - }); - - // Clear the context - RequestContext::setContext(nullptr); - - EXPECT_EQ(nullptr, RequestContext::get()->getContextData("test")); - - // Fulfill the promise - p.setValue(); -} - - -// This only fails about 1 in 1k times when the bug is present :( -TEST(Future, t5506504) { - ThreadExecutor x; - - auto fn = [&x]{ - auto promises = std::make_shared>>(4); - vector> futures; - - for (auto& p : *promises) { - futures.emplace_back( - p.getFuture() - .via(&x) - .then([](Try&&){})); - } - - x.waitForStartup(); - x.add([promises]{ - for (auto& p : *promises) p.setValue(); - }); - - return collectAll(futures); - }; - - fn().wait(); -} - // Test of handling of a circular dependency. It's never recommended // to have one because of possible memory leaks. Here we test that // we can handle freeing of the Future while it is running. @@ -1601,333 +654,3 @@ TEST(Future, thenDynamic) { p.setValue(2); EXPECT_EQ(f.get(), 5); } - -TEST(Future, via_then_get_was_racy) { - ThreadExecutor x; - std::unique_ptr val = folly::via(&x) - .then([] { return folly::make_unique(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 p1; - Promise p2; - p1.setValue(27); - p2.setValue(27); - auto f1 = p1.getFuture(); - auto f2 = p2.getFuture(); - EXPECT_TRUE(f1.willEqual(f2).get()); - }{ - Promise p1; - Promise 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 p1; - Promise 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 p1; - Promise 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 p1; - Promise 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 p1; - Promise 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 p1; - Promise 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 p1; - Promise 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 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, 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, 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, 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, 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, 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, 0.0, - [](double a, int&& b){ - return makeFuture(a + b + 0.1); - }); - EXPECT_EQ(6.3, f2.get()); - } -} - -TEST(Reduce, Chain) { - auto makeFutures = [](int count) { - std::vector> 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&& 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, UnorderedReduce) { - { - std::vector> fs; - fs.push_back(makeFuture(1)); - fs.push_back(makeFuture(2)); - fs.push_back(makeFuture(3)); - - Future f = unorderedReduce(fs.begin(), fs.end(), 0.0, - [](double a, int&& b){ - return double(b); - }); - EXPECT_EQ(3.0, f.get()); - } - { - Promise p1; - Promise p2; - Promise p3; - - std::vector> fs; - fs.push_back(p1.getFuture()); - fs.push_back(p2.getFuture()); - fs.push_back(p3.getFuture()); - - Future 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, UnorderedReduceException) { - Promise p1; - Promise p2; - Promise p3; - - std::vector> fs; - fs.push_back(p1.getFuture()); - fs.push_back(p2.getFuture()); - fs.push_back(p3.getFuture()); - - Future 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 p1; - Promise p2; - Promise p3; - - std::vector> fs; - fs.push_back(p1.getFuture()); - fs.push_back(p2.getFuture()); - fs.push_back(p3.getFuture()); - - int c = 0; - std::vector> 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()); -} - -TEST(Promise, isFulfilled) { - Promise p; - - EXPECT_FALSE(p.isFulfilled()); - p.setValue(42); - EXPECT_TRUE(p.isFulfilled()); -} - -TEST(Promise, isFulfilledWithFuture) { - Promise p; - auto f = p.getFuture(); // so core_ will become null - - EXPECT_FALSE(p.isFulfilled()); - p.setValue(42); // after here - EXPECT_TRUE(p.isFulfilled()); -} diff --git a/folly/futures/test/ClientCompile.cpp b/folly/futures/test/HeaderCompileTest.cpp similarity index 91% rename from folly/futures/test/ClientCompile.cpp rename to folly/futures/test/HeaderCompileTest.cpp index caaac9ef..2138bccd 100644 --- a/folly/futures/test/ClientCompile.cpp +++ b/folly/futures/test/HeaderCompileTest.cpp @@ -14,9 +14,14 @@ * limitations under the License. */ +#include + // amazing what things can go wrong if you include things in an unexpected // order. #include #include #include -int main() { return 0; } + +TEST(Basic, compiles) { + EXPECT_TRUE(true); +} diff --git a/folly/futures/test/Interrupts.cpp b/folly/futures/test/InterruptTest.cpp similarity index 87% rename from folly/futures/test/Interrupts.cpp rename to folly/futures/test/InterruptTest.cpp index 3b08bc64..1963c0a7 100644 --- a/folly/futures/test/Interrupts.cpp +++ b/folly/futures/test/InterruptTest.cpp @@ -20,9 +20,8 @@ #include using namespace folly; -using folly::exception_wrapper; -TEST(Interrupts, raise) { +TEST(Interrupt, raise) { std::runtime_error eggs("eggs"); Promise p; p.setInterruptHandler([&](const exception_wrapper& e) { @@ -31,7 +30,7 @@ TEST(Interrupts, raise) { p.getFuture().raise(eggs); } -TEST(Interrupts, cancel) { +TEST(Interrupt, cancel) { Promise p; p.setInterruptHandler([&](const exception_wrapper& e) { EXPECT_THROW(e.throwException(), FutureCancellation); @@ -39,7 +38,7 @@ TEST(Interrupts, cancel) { p.getFuture().cancel(); } -TEST(Interrupts, handleThenInterrupt) { +TEST(Interrupt, handleThenInterrupt) { Promise p; bool flag = false; p.setInterruptHandler([&](const exception_wrapper& e) { flag = true; }); @@ -47,7 +46,7 @@ TEST(Interrupts, handleThenInterrupt) { EXPECT_TRUE(flag); } -TEST(Interrupts, interruptThenHandle) { +TEST(Interrupt, interruptThenHandle) { Promise p; bool flag = false; p.getFuture().cancel(); @@ -55,7 +54,7 @@ TEST(Interrupts, interruptThenHandle) { EXPECT_TRUE(flag); } -TEST(Interrupts, interruptAfterFulfilNoop) { +TEST(Interrupt, interruptAfterFulfilNoop) { Promise p; bool flag = false; p.setInterruptHandler([&](const exception_wrapper& e) { flag = true; }); @@ -64,7 +63,7 @@ TEST(Interrupts, interruptAfterFulfilNoop) { EXPECT_FALSE(flag); } -TEST(Interrupts, secondInterruptNoop) { +TEST(Interrupt, secondInterruptNoop) { Promise p; int count = 0; p.setInterruptHandler([&](const exception_wrapper& e) { count++; }); diff --git a/folly/futures/test/MapTest.cpp b/folly/futures/test/MapTest.cpp new file mode 100644 index 00000000..ed0313d7 --- /dev/null +++ b/folly/futures/test/MapTest.cpp @@ -0,0 +1,47 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +using namespace folly; + +TEST(Map, basic) { + Promise p1; + Promise p2; + Promise p3; + + std::vector> fs; + fs.push_back(p1.getFuture()); + fs.push_back(p2.getFuture()); + fs.push_back(p3.getFuture()); + + int c = 0; + std::vector> 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()); +} diff --git a/folly/futures/test/SugarTest.cpp b/folly/futures/test/PollTest.cpp similarity index 67% rename from folly/futures/test/SugarTest.cpp rename to folly/futures/test/PollTest.cpp index 0563eef4..166dea67 100644 --- a/folly/futures/test/SugarTest.cpp +++ b/folly/futures/test/PollTest.cpp @@ -20,38 +20,22 @@ using namespace folly; -TEST(Sugar, pollReady) { +TEST(Poll, ready) { Promise p; auto f = p.getFuture(); p.setValue(42); EXPECT_EQ(42, f.poll().value().value()); } -TEST(Sugar, pollNotReady) { +TEST(Poll, notReady) { Promise p; auto f = p.getFuture(); EXPECT_FALSE(f.poll().hasValue()); } -TEST(Sugar, pollException) { +TEST(Poll, exception) { Promise p; auto f = p.getFuture(); p.setWith([] { throw std::runtime_error("Runtime"); }); EXPECT_TRUE(f.poll().value().hasException()); } - -TEST(Sugar, filterTrue) { - EXPECT_EQ(42, makeFuture(42).filter([](int){ return true; }).get()); -} - -TEST(Sugar, filterFalse) { - EXPECT_THROW(makeFuture(42).filter([](int){ return false; }).get(), - folly::PredicateDoesNotObtain); -} - -TEST(Sugar, filterMoveonly) { - EXPECT_EQ(42, - *makeFuture(folly::make_unique(42)) - .filter([](std::unique_ptr const&) { return true; }) - .get()); -} diff --git a/folly/futures/test/PromiseTest.cpp b/folly/futures/test/PromiseTest.cpp new file mode 100644 index 00000000..5e207068 --- /dev/null +++ b/folly/futures/test/PromiseTest.cpp @@ -0,0 +1,128 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +using namespace folly; +using std::unique_ptr; +using std::string; + +typedef FutureException eggs_t; +static eggs_t eggs("eggs"); + +TEST(Promise, special) { + EXPECT_FALSE(std::is_copy_constructible>::value); + EXPECT_FALSE(std::is_copy_assignable>::value); + EXPECT_TRUE(std::is_move_constructible>::value); + EXPECT_TRUE(std::is_move_assignable>::value); +} + +TEST(Promise, getFuture) { + Promise p; + Future f = p.getFuture(); + EXPECT_FALSE(f.isReady()); +} + +TEST(Promise, setValue) { + Promise fund; + auto ffund = fund.getFuture(); + fund.setValue(42); + EXPECT_EQ(42, ffund.value()); + + struct Foo { + string name; + int value; + }; + + Promise pod; + auto fpod = pod.getFuture(); + Foo f = {"the answer", 42}; + pod.setValue(f); + Foo f2 = fpod.value(); + EXPECT_EQ(f.name, f2.name); + EXPECT_EQ(f.value, f2.value); + + pod = Promise(); + fpod = pod.getFuture(); + pod.setValue(std::move(f2)); + Foo f3 = fpod.value(); + EXPECT_EQ(f.name, f3.name); + EXPECT_EQ(f.value, f3.value); + + Promise> mov; + auto fmov = mov.getFuture(); + mov.setValue(unique_ptr(new int(42))); + unique_ptr ptr = std::move(fmov.value()); + EXPECT_EQ(42, *ptr); + + Promise v; + auto fv = v.getFuture(); + v.setValue(); + EXPECT_TRUE(fv.isReady()); +} + +TEST(Promise, setException) { + { + Promise p; + auto f = p.getFuture(); + p.setException(eggs); + EXPECT_THROW(f.value(), eggs_t); + } + { + Promise p; + auto f = p.getFuture(); + try { + throw eggs; + } catch (...) { + p.setException(exception_wrapper(std::current_exception())); + } + EXPECT_THROW(f.value(), eggs_t); + } +} + +TEST(Promise, setWith) { + { + Promise p; + auto f = p.getFuture(); + p.setWith([] { return 42; }); + EXPECT_EQ(42, f.value()); + } + { + Promise p; + auto f = p.getFuture(); + p.setWith([]() -> int { throw eggs; }); + EXPECT_THROW(f.value(), eggs_t); + } +} + +TEST(Promise, isFulfilled) { + Promise p; + + EXPECT_FALSE(p.isFulfilled()); + p.setValue(42); + EXPECT_TRUE(p.isFulfilled()); +} + +TEST(Promise, isFulfilledWithFuture) { + Promise p; + auto f = p.getFuture(); // so core_ will become null + + EXPECT_FALSE(p.isFulfilled()); + p.setValue(42); // after here + EXPECT_TRUE(p.isFulfilled()); +} diff --git a/folly/futures/test/ReduceTest.cpp b/folly/futures/test/ReduceTest.cpp new file mode 100644 index 00000000..20e2924f --- /dev/null +++ b/folly/futures/test/ReduceTest.cpp @@ -0,0 +1,174 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +using namespace folly; + +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, 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, 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, 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, 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, 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, 0.0, + [](double a, int&& b){ + return makeFuture(a + b + 0.1); + }); + EXPECT_EQ(6.3, f2.get()); + } +} + +TEST(Reduce, chain) { + auto makeFutures = [](int count) { + std::vector> 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&& 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, unorderedReduce) { + { + std::vector> fs; + fs.push_back(makeFuture(1)); + fs.push_back(makeFuture(2)); + fs.push_back(makeFuture(3)); + + Future f = unorderedReduce(fs.begin(), fs.end(), 0.0, + [](double a, int&& b){ + return double(b); + }); + EXPECT_EQ(3.0, f.get()); + } + { + Promise p1; + Promise p2; + Promise p3; + + std::vector> fs; + fs.push_back(p1.getFuture()); + fs.push_back(p2.getFuture()); + fs.push_back(p3.getFuture()); + + Future 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, unorderedReduceException) { + Promise p1; + Promise p2; + Promise p3; + + std::vector> fs; + fs.push_back(p1.getFuture()); + fs.push_back(p2.getFuture()); + fs.push_back(p3.getFuture()); + + Future 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); +} diff --git a/folly/futures/test/SharedPromiseTest.cpp b/folly/futures/test/SharedPromiseTest.cpp index 280bb64c..495526d5 100644 --- a/folly/futures/test/SharedPromiseTest.cpp +++ b/folly/futures/test/SharedPromiseTest.cpp @@ -13,12 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include + #include +#include + using namespace folly; -TEST(SharedPromise, SetGet) { +TEST(SharedPromise, setGet) { SharedPromise p; p.setValue(1); auto f1 = p.getFuture(); @@ -26,7 +28,7 @@ TEST(SharedPromise, SetGet) { EXPECT_EQ(1, f1.value()); EXPECT_EQ(1, f2.value()); } -TEST(SharedPromise, GetSet) { +TEST(SharedPromise, getSet) { SharedPromise p; auto f1 = p.getFuture(); auto f2 = p.getFuture(); @@ -35,7 +37,7 @@ TEST(SharedPromise, GetSet) { EXPECT_EQ(1, f2.value()); } -TEST(SharedPromise, GetSetGet) { +TEST(SharedPromise, getSetGet) { SharedPromise p; auto f1 = p.getFuture(); p.setValue(1); @@ -44,7 +46,7 @@ TEST(SharedPromise, GetSetGet) { EXPECT_EQ(1, f2.value()); } -TEST(SharedPromise, Reset) { +TEST(SharedPromise, reset) { SharedPromise p; auto f1 = p.getFuture(); @@ -58,7 +60,7 @@ TEST(SharedPromise, Reset) { EXPECT_EQ(2, f2.value()); } -TEST(SharedPromise, GetMoveSet) { +TEST(SharedPromise, getMoveSet) { SharedPromise p; auto f = p.getFuture(); auto p2 = std::move(p); @@ -66,7 +68,7 @@ TEST(SharedPromise, GetMoveSet) { EXPECT_EQ(1, f.value()); } -TEST(SharedPromise, SetMoveGet) { +TEST(SharedPromise, setMoveGet) { SharedPromise p; p.setValue(1); auto p2 = std::move(p); @@ -74,7 +76,7 @@ TEST(SharedPromise, SetMoveGet) { EXPECT_EQ(1, f.value()); } -TEST(SharedPromise, MoveSetGet) { +TEST(SharedPromise, moveSetGet) { SharedPromise p; auto p2 = std::move(p); p2.setValue(1); @@ -82,7 +84,7 @@ TEST(SharedPromise, MoveSetGet) { EXPECT_EQ(1, f.value()); } -TEST(SharedPromise, MoveGetSet) { +TEST(SharedPromise, moveGetSet) { SharedPromise p; auto p2 = std::move(p); auto f = p2.getFuture(); @@ -90,7 +92,7 @@ TEST(SharedPromise, MoveGetSet) { EXPECT_EQ(1, f.value()); } -TEST(SharedPromise, MoveMove) { +TEST(SharedPromise, moveMove) { SharedPromise> p; auto f1 = p.getFuture(); auto f2 = p.getFuture(); diff --git a/folly/futures/test/Thens.cpp b/folly/futures/test/ThenCompileTest.cpp similarity index 97% rename from folly/futures/test/Thens.cpp rename to folly/futures/test/ThenCompileTest.cpp index 3d136b56..0678d159 100644 --- a/folly/futures/test/Thens.cpp +++ b/folly/futures/test/ThenCompileTest.cpp @@ -1,10 +1,11 @@ -// This file is @generated by thens.rb. Do not edit directly. +// This file is @generated by then_compile_test.rb. Do not edit directly. -#include +#include -TEST(Future, thenVariants) { +using namespace folly; + +TEST(Basic, thenVariants) { SomeClass anObject; - folly::Executor* anExecutor; {Future f = someFuture().then(&aFunction, Try&&>);} {Future f = someFuture().then(&SomeClass::aStaticMethod, Try&&>);} @@ -89,4 +90,3 @@ TEST(Future, thenVariants) { {Future f = someFuture().then([&](A&){return B();});} {Future f = someFuture().then([&](){return B();});} } - diff --git a/folly/futures/test/Thens.h b/folly/futures/test/ThenCompileTest.h similarity index 95% rename from folly/futures/test/Thens.h rename to folly/futures/test/ThenCompileTest.h index 5391e246..c8327e73 100644 --- a/folly/futures/test/Thens.h +++ b/folly/futures/test/ThenCompileTest.h @@ -15,16 +15,16 @@ */ #pragma once + #include -#include + #include -#include -using namespace folly; -using namespace std; -using namespace testing; +#include -typedef unique_ptr A; +namespace folly { + +typedef std::unique_ptr A; struct B {}; template @@ -95,3 +95,5 @@ public: return makeFuture(T()); } }; + +} diff --git a/folly/futures/test/ThenTest.cpp b/folly/futures/test/ThenTest.cpp index fdc65a64..6d330f64 100644 --- a/folly/futures/test/ThenTest.cpp +++ b/folly/futures/test/ThenTest.cpp @@ -15,10 +15,11 @@ */ #include -#include #include +#include + using namespace folly; struct Widget { @@ -48,7 +49,7 @@ TEST(Then, makeFuture) { EXPECT_EQ(future.value().moved_, 2); } -TEST(Then, TryConstRValueReference) { +TEST(Then, tryConstRValueReference) { auto future = makeFuture(23).then( [](const Try&& t) { EXPECT_EQ(t.value().copied_, 0); @@ -58,7 +59,7 @@ TEST(Then, TryConstRValueReference) { EXPECT_EQ(future.value(), 23); } -TEST(Then, TryRValueReference) { +TEST(Then, tryRValueReference) { auto future = makeFuture(23).then( [](Try&& t) { EXPECT_EQ(t.value().copied_, 0); @@ -68,7 +69,7 @@ TEST(Then, TryRValueReference) { EXPECT_EQ(future.value(), 23); } -TEST(Then, TryLValueReference) { +TEST(Then, tryLValueReference) { auto future = makeFuture(23).then( [](Try& t) { EXPECT_EQ(t.value().copied_, 0); @@ -78,7 +79,7 @@ TEST(Then, TryLValueReference) { EXPECT_EQ(future.value(), 23); } -TEST(Then, TryConstLValueReference) { +TEST(Then, tryConstLValueReference) { auto future = makeFuture(23).then( [](const Try& t) { EXPECT_EQ(t.value().copied_, 0); @@ -88,7 +89,7 @@ TEST(Then, TryConstLValueReference) { EXPECT_EQ(future.value(), 23); } -TEST(Then, TryValue) { +TEST(Then, tryValue) { auto future = makeFuture(23).then( [](Try t) { EXPECT_EQ(t.value().copied_, 0); @@ -98,7 +99,7 @@ TEST(Then, TryValue) { EXPECT_EQ(future.value(), 23); } -TEST(Then, TryConstValue) { +TEST(Then, tryConstValue) { auto future = makeFuture(23).then( [](const Try t) { EXPECT_EQ(t.value().copied_, 0); @@ -108,7 +109,7 @@ TEST(Then, TryConstValue) { EXPECT_EQ(future.value(), 23); } -TEST(Then, ConstRValueReference) { +TEST(Then, constRValueReference) { auto future = makeFuture(23).then( [](const Widget&& w) { EXPECT_EQ(w.copied_, 0); @@ -118,7 +119,7 @@ TEST(Then, ConstRValueReference) { EXPECT_EQ(future.value(), 23); } -TEST(Then, RValueReference) { +TEST(Then, rValueReference) { auto future = makeFuture(23).then( [](Widget&& w) { EXPECT_EQ(w.copied_, 0); @@ -128,7 +129,7 @@ TEST(Then, RValueReference) { EXPECT_EQ(future.value(), 23); } -TEST(Then, LValueReference) { +TEST(Then, lValueReference) { auto future = makeFuture(23).then( [](Widget& w) { EXPECT_EQ(w.copied_, 0); @@ -138,7 +139,7 @@ TEST(Then, LValueReference) { EXPECT_EQ(future.value(), 23); } -TEST(Then, ConstLValueReference) { +TEST(Then, constLValueReference) { auto future = makeFuture(23).then( [](const Widget& w) { EXPECT_EQ(w.copied_, 0); @@ -148,7 +149,7 @@ TEST(Then, ConstLValueReference) { EXPECT_EQ(future.value(), 23); } -TEST(Then, Value) { +TEST(Then, value) { auto future = makeFuture(23).then( [](Widget w) { EXPECT_EQ(w.copied_, 0); @@ -158,7 +159,7 @@ TEST(Then, Value) { EXPECT_EQ(future.value(), 23); } -TEST(Then, ConstValue) { +TEST(Then, constValue) { auto future = makeFuture(23).then( [](const Widget w) { EXPECT_EQ(w.copied_, 0); diff --git a/folly/futures/test/TimekeeperTest.cpp b/folly/futures/test/TimekeeperTest.cpp index f9db640d..0ecdb2d7 100644 --- a/folly/futures/test/TimekeeperTest.cpp +++ b/folly/futures/test/TimekeeperTest.cpp @@ -13,15 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #include #include + #include using namespace folly; -using namespace std::chrono; -using folly::Timekeeper; -using Duration = folly::Duration; std::chrono::milliseconds const one_ms(1); std::chrono::milliseconds const awhile(10); @@ -66,7 +65,7 @@ TEST(Timekeeper, futureGetBeforeTimeout) { // runs it by hand they're not sitting there forever wondering why it's // blocked, and get a useful error message instead. If it does get flaky, // empirically increase the timeout to the point where it's very improbable. - EXPECT_EQ(42, p.getFuture().get(seconds(2))); + EXPECT_EQ(42, p.getFuture().get(std::chrono::seconds(2))); t.join(); } diff --git a/folly/futures/test/Try.cpp b/folly/futures/test/TryTest.cpp similarity index 88% rename from folly/futures/test/Try.cpp rename to folly/futures/test/TryTest.cpp index f0bb5d66..479fcb99 100644 --- a/folly/futures/test/Try.cpp +++ b/folly/futures/test/TryTest.cpp @@ -21,6 +21,26 @@ using namespace folly; +TEST(Try, basic) { + class A { + public: + A(int x) : x_(x) {} + + int x() const { + return x_; + } + private: + int x_; + }; + + A a(5); + Try t_a(std::move(a)); + + Try t_void; + + EXPECT_EQ(5, t_a.value().x()); +} + // Make sure we can copy Trys for copyable types TEST(Try, copy) { Try t; diff --git a/folly/futures/test/UnitTest.cpp b/folly/futures/test/UnitTest.cpp index 930b1231..ae49065b 100644 --- a/folly/futures/test/UnitTest.cpp +++ b/folly/futures/test/UnitTest.cpp @@ -14,12 +14,13 @@ * limitations under the License. */ -#include #include +#include + using namespace folly; -TEST(Unit, FutureDefaultCtor) { +TEST(Unit, futureDefaultCtor) { Future(); } @@ -29,19 +30,19 @@ TEST(Unit, voidOrUnit) { EXPECT_FALSE(is_void_or_unit::value); } -TEST(Unit, PromiseSetValue) { +TEST(Unit, promiseSetValue) { Promise p; p.setValue(); } -TEST(Unit, LiftInt) { +TEST(Unit, liftInt) { using Lifted = Unit::Lift; EXPECT_FALSE(Lifted::value); auto v = std::is_same::value; EXPECT_TRUE(v); } -TEST(Unit, LiftVoid) { +TEST(Unit, liftVoid) { using Lifted = Unit::Lift; EXPECT_TRUE(Lifted::value); auto v = std::is_same::value; diff --git a/folly/futures/test/UnwrapTest.cpp b/folly/futures/test/UnwrapTest.cpp new file mode 100644 index 00000000..686592cc --- /dev/null +++ b/folly/futures/test/UnwrapTest.cpp @@ -0,0 +1,51 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +using namespace folly; + +// A simple scenario for the unwrap call, when the promise was fulfilled +// before calling to unwrap. +TEST(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(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(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()); +} diff --git a/folly/futures/test/ViaTest.cpp b/folly/futures/test/ViaTest.cpp index edaed774..9acac1bc 100644 --- a/folly/futures/test/ViaTest.cpp +++ b/folly/futures/test/ViaTest.cpp @@ -15,12 +15,15 @@ */ #include -#include #include #include #include #include +#include +#include + +#include using namespace folly; @@ -73,12 +76,12 @@ struct ViaFixture : public testing::Test { std::thread t; }; -TEST(Via, exception_on_launch) { +TEST(Via, exceptionOnLaunch) { auto future = makeFuture(std::runtime_error("E")); EXPECT_THROW(future.value(), std::runtime_error); } -TEST(Via, then_value) { +TEST(Via, thenValue) { auto future = makeFuture(std::move(1)) .then([](Try&& t) { return t.value() == 1; @@ -88,7 +91,7 @@ TEST(Via, then_value) { EXPECT_TRUE(future.value()); } -TEST(Via, then_future) { +TEST(Via, thenFuture) { auto future = makeFuture(1) .then([](Try&& t) { return makeFuture(t.value() == 1); @@ -100,7 +103,7 @@ static Future doWorkStatic(Try&& t) { return makeFuture(t.value() + ";static"); } -TEST(Via, then_function) { +TEST(Via, thenFunction) { struct Worker { Future doWork(Try&& t) { return makeFuture(t.value() + ";class"); @@ -119,7 +122,7 @@ TEST(Via, then_function) { EXPECT_EQ(f.value(), "start;static;class-static;class"); } -TEST_F(ViaFixture, thread_hops) { +TEST_F(ViaFixture, threadHops) { auto westThreadId = std::this_thread::get_id(); auto f = via(eastExecutor.get()).then([=](Try&& t) { EXPECT_NE(std::this_thread::get_id(), westThreadId); @@ -132,7 +135,7 @@ TEST_F(ViaFixture, thread_hops) { EXPECT_EQ(f.getVia(waiter.get()), 1); } -TEST_F(ViaFixture, chain_vias) { +TEST_F(ViaFixture, chainVias) { auto westThreadId = std::this_thread::get_id(); auto f = via(eastExecutor.get()).then([=]() { EXPECT_NE(std::this_thread::get_id(), westThreadId); @@ -287,3 +290,127 @@ TEST(Via, then2Variadic) { x.run(); EXPECT_TRUE(f.a); } + +/// Simple executor that does work in another thread +class ThreadExecutor : public Executor { + folly::MPMCQueue funcs; + std::atomic done {false}; + std::thread worker; + folly::Baton<> baton; + + void work() { + baton.post(); + Func fn; + while (!done) { + while (!funcs.isEmpty()) { + funcs.blockingRead(fn); + fn(); + } + } + } + + public: + explicit ThreadExecutor(size_t n = 1024) + : funcs(n) { + worker = std::thread(std::bind(&ThreadExecutor::work, this)); + } + + ~ThreadExecutor() { + done = true; + funcs.write([]{}); + worker.join(); + } + + void add(Func fn) override { + funcs.blockingWrite(std::move(fn)); + } + + void waitForStartup() { + baton.wait(); + } +}; + +TEST(Via, viaThenGetWasRacy) { + ThreadExecutor x; + std::unique_ptr val = folly::via(&x) + .then([] { return folly::make_unique(42); }) + .get(); + ASSERT_TRUE(!!val); + EXPECT_EQ(42, *val); +} + +class DummyDrivableExecutor : public DrivableExecutor { + public: + void add(Func f) override {} + void drive() override { ran = true; } + bool ran{false}; +}; + +TEST(Via, getVia) { + { + // non-void + ManualExecutor x; + auto f = via(&x).then([]{ return true; }); + EXPECT_TRUE(f.getVia(&x)); + } + + { + // void + ManualExecutor x; + auto f = via(&x).then(); + f.getVia(&x); + } + + { + DummyDrivableExecutor x; + auto f = makeFuture(true); + EXPECT_TRUE(f.getVia(&x)); + EXPECT_FALSE(x.ran); + } +} + +TEST(Via, waitVia) { + { + ManualExecutor x; + auto f = via(&x).then(); + EXPECT_FALSE(f.isReady()); + f.waitVia(&x); + EXPECT_TRUE(f.isReady()); + } + + { + // try rvalue as well + ManualExecutor x; + auto f = via(&x).then().waitVia(&x); + EXPECT_TRUE(f.isReady()); + } + + { + DummyDrivableExecutor x; + makeFuture(true).waitVia(&x); + EXPECT_FALSE(x.ran); + } +} + +TEST(Via, viaRaces) { + ManualExecutor x; + Promise p; + auto tid = std::this_thread::get_id(); + bool done = false; + + std::thread t1([&] { + p.getFuture() + .via(&x) + .then([&](Try&&) { EXPECT_EQ(tid, std::this_thread::get_id()); }) + .then([&](Try&&) { EXPECT_EQ(tid, std::this_thread::get_id()); }) + .then([&](Try&&) { done = true; }); + }); + + std::thread t2([&] { + p.setValue(); + }); + + while (!done) x.run(); + t1.join(); + t2.join(); +} diff --git a/folly/futures/test/WaitTest.cpp b/folly/futures/test/WaitTest.cpp new file mode 100644 index 00000000..d2b91f99 --- /dev/null +++ b/folly/futures/test/WaitTest.cpp @@ -0,0 +1,197 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +using namespace folly; +using std::vector; +using std::chrono::milliseconds; + +TEST(Wait, waitImmediate) { + makeFuture().wait(); + auto done = makeFuture(42).wait().value(); + EXPECT_EQ(42, done); + + vector v{1,2,3}; + auto done_v = makeFuture(v).wait().value(); + EXPECT_EQ(v.size(), done_v.size()); + EXPECT_EQ(v, done_v); + + vector> v_f; + v_f.push_back(makeFuture()); + v_f.push_back(makeFuture()); + auto done_v_f = collectAll(v_f).wait().value(); + EXPECT_EQ(2, done_v_f.size()); + + vector> v_fb; + v_fb.push_back(makeFuture(true)); + v_fb.push_back(makeFuture(false)); + auto fut = collectAll(v_fb); + auto done_v_fb = std::move(fut.wait().value()); + EXPECT_EQ(2, done_v_fb.size()); +} + +TEST(Wait, wait) { + Promise p; + Future f = p.getFuture(); + std::atomic flag{false}; + std::atomic result{1}; + std::atomic id; + + std::thread t([&](Future&& tf){ + auto n = tf.then([&](Try && t) { + id = std::this_thread::get_id(); + return t.value(); + }); + flag = true; + result.store(n.wait().value()); + }, + std::move(f) + ); + while(!flag){} + EXPECT_EQ(result.load(), 1); + p.setValue(42); + t.join(); + // validate that the callback ended up executing in this thread, which + // is more to ensure that this test actually tests what it should + EXPECT_EQ(id, std::this_thread::get_id()); + EXPECT_EQ(result.load(), 42); +} + +struct MoveFlag { + MoveFlag() = default; + MoveFlag& operator=(const MoveFlag&) = delete; + MoveFlag(const MoveFlag&) = delete; + MoveFlag(MoveFlag&& other) noexcept { + other.moved = true; + } + bool moved{false}; +}; + +TEST(Wait, waitReplacesSelf) { + // wait + { + // lvalue + auto f1 = makeFuture(MoveFlag()); + f1.wait(); + EXPECT_FALSE(f1.value().moved); + + // rvalue + auto f2 = makeFuture(MoveFlag()).wait(); + EXPECT_FALSE(f2.value().moved); + } + + // wait(Duration) + { + // lvalue + auto f1 = makeFuture(MoveFlag()); + f1.wait(milliseconds(1)); + EXPECT_FALSE(f1.value().moved); + + // rvalue + auto f2 = makeFuture(MoveFlag()).wait(milliseconds(1)); + EXPECT_FALSE(f2.value().moved); + } + + // waitVia + { + folly::EventBase eb; + // lvalue + auto f1 = makeFuture(MoveFlag()); + f1.waitVia(&eb); + EXPECT_FALSE(f1.value().moved); + + // rvalue + auto f2 = makeFuture(MoveFlag()).waitVia(&eb); + EXPECT_FALSE(f2.value().moved); + } +} + +TEST(Wait, waitWithDuration) { + { + Promise p; + Future f = p.getFuture(); + f.wait(milliseconds(1)); + EXPECT_FALSE(f.isReady()); + p.setValue(1); + EXPECT_TRUE(f.isReady()); + } + { + Promise p; + Future f = p.getFuture(); + p.setValue(1); + f.wait(milliseconds(1)); + EXPECT_TRUE(f.isReady()); + } + { + vector> v_fb; + v_fb.push_back(makeFuture(true)); + v_fb.push_back(makeFuture(false)); + auto f = collectAll(v_fb); + f.wait(milliseconds(1)); + EXPECT_TRUE(f.isReady()); + EXPECT_EQ(2, f.value().size()); + } + { + vector> v_fb; + Promise p1; + Promise p2; + v_fb.push_back(p1.getFuture()); + v_fb.push_back(p2.getFuture()); + auto f = collectAll(v_fb); + f.wait(milliseconds(1)); + EXPECT_FALSE(f.isReady()); + p1.setValue(true); + EXPECT_FALSE(f.isReady()); + p2.setValue(true); + EXPECT_TRUE(f.isReady()); + } + { + auto f = makeFuture().wait(milliseconds(1)); + EXPECT_TRUE(f.isReady()); + } + + { + Promise p; + auto start = std::chrono::steady_clock::now(); + auto f = p.getFuture().wait(milliseconds(100)); + auto elapsed = std::chrono::steady_clock::now() - start; + EXPECT_GE(elapsed, milliseconds(100)); + EXPECT_FALSE(f.isReady()); + p.setValue(); + EXPECT_TRUE(f.isReady()); + } + + { + // Try to trigger the race where the resultant Future is not yet complete + // even if we didn't hit the timeout, and make sure we deal with it properly + Promise p; + folly::Baton<> b; + auto t = std::thread([&]{ + b.post(); + /* sleep override */ std::this_thread::sleep_for(milliseconds(100)); + p.setValue(); + }); + b.wait(); + auto f = p.getFuture().wait(std::chrono::seconds(3600)); + EXPECT_TRUE(f.isReady()); + t.join(); + } +} diff --git a/folly/futures/test/WillEqualTest.cpp b/folly/futures/test/WillEqualTest.cpp new file mode 100644 index 00000000..f517c7d9 --- /dev/null +++ b/folly/futures/test/WillEqualTest.cpp @@ -0,0 +1,106 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +using namespace folly; + +TEST(WillEqual, basic) { + // both p1 and p2 already fulfilled + { + Promise p1; + Promise p2; + p1.setValue(27); + p2.setValue(27); + auto f1 = p1.getFuture(); + auto f2 = p2.getFuture(); + EXPECT_TRUE(f1.willEqual(f2).get()); + } + { + Promise p1; + Promise 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 p1; + Promise 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 p1; + Promise 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 p1; + Promise 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 p1; + Promise 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 p1; + Promise 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 p1; + Promise p2; + p2.setValue(36); + auto f1 = p1.getFuture(); + auto f2 = p2.getFuture(); + auto f3 = f1.willEqual(f2); + p1.setValue(27); + EXPECT_FALSE(f3.get()); + } +} diff --git a/folly/futures/test/WindowTest.cpp b/folly/futures/test/WindowTest.cpp new file mode 100644 index 00000000..cc480fbe --- /dev/null +++ b/folly/futures/test/WindowTest.cpp @@ -0,0 +1,81 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include + +using namespace folly; + +TEST(Window, basic) { + // int -> Future + auto fn = [](std::vector input, size_t window_size, size_t expect) { + auto res = reduce( + window( + input, + [](int i) { return makeFuture(i); }, + 2), + 0, + [](int sum, const Try& b) { + return sum + *b; + }).get(); + EXPECT_EQ(expect, res); + }; + { + // 2 in-flight at a time + std::vector input = {1, 2, 3}; + fn(input, 2, 6); + } + { + // 4 in-flight at a time + std::vector input = {1, 2, 3}; + fn(input, 4, 6); + } + { + // empty inpt + std::vector input; + fn(input, 1, 0); + } + { + // int -> Future + auto res = reduce( + window( + std::vector({1, 2, 3}), + [](int i) { return makeFuture(); }, + 2), + 0, + [](int sum, const Try& b) { + EXPECT_TRUE(b.hasValue()); + return sum + 1; + }).get(); + EXPECT_EQ(3, res); + } + { + // string -> return Future + auto res = reduce( + window( + std::vector{"1", "2", "3"}, + [](std::string s) { return makeFuture(folly::to(s)); }, + 2), + 0, + [](int sum, const Try& b) { + return sum + *b; + }).get(); + EXPECT_EQ(6, res); + } +} diff --git a/folly/futures/test/thens.rb b/folly/futures/test/then_compile_test.rb similarity index 83% rename from folly/futures/test/thens.rb rename to folly/futures/test/then_compile_test.rb index 4eeffd89..436e6ad0 100755 --- a/folly/futures/test/thens.rb +++ b/folly/futures/test/then_compile_test.rb @@ -1,6 +1,6 @@ #!/usr/bin/env ruby -# ruby folly/futures/test/thens.rb > folly/futures/test/Thens.cpp +# cd folly/futures/test && ruby then_compile_test.rb > ThenCompileTest.cpp # An exercise in combinatorics. # (ordinary/static function, member function, std::function, lambda) @@ -62,15 +62,15 @@ tests = ( ).map {|a| test(a)}.flatten print < +#include -TEST(Future, thenVariants) { +using namespace folly; + +TEST(Basic, thenVariants) { SomeClass anObject; - folly::Executor* anExecutor; #{tests.join("\n ")} } - EOF -- 2.34.1