TEST(WindowExecutor, parallel) { ManualExecutor executor; std::vector<int> input; std::vector<Promise<int>> ps(10); for (size_t i = 0; i < ps.size(); i++) { input.emplace_back(i); } auto f = collect( window(&executor, input, [&](int i) { return ps[i].getFuture(); }, 3)); std::vector<std::thread> ts; boost::barrier barrier(ps.size() + 1); for (size_t i = 0; i < ps.size(); i++) { ts.emplace_back([&ps, &barrier, i]() { barrier.wait(); ps[i].setValue(i); }); } barrier.wait(); for (auto& t : ts) { t.join(); } executor.drain(); EXPECT_TRUE(f.isReady()); for (size_t i = 0; i < ps.size(); i++) { EXPECT_EQ(i, f.value()[i]); } }
TEST(WindowExecutor, parallelWithError) { ManualExecutor executor; std::vector<int> input; std::vector<Promise<int>> ps(10); for (size_t i = 0; i < ps.size(); i++) { input.emplace_back(i); } auto f = collect( window(&executor, input, [&](int i) { return ps[i].getFuture(); }, 3)); std::vector<std::thread> ts; boost::barrier barrier(ps.size() + 1); for (size_t i = 0; i < ps.size(); i++) { ts.emplace_back([&ps, &barrier, i]() { barrier.wait(); if (i == (ps.size() / 2)) { ps[i].setException(eggs); } else { ps[i].setValue(i); } }); } barrier.wait(); for (auto& t : ts) { t.join(); } executor.drain(); EXPECT_TRUE(f.isReady()); EXPECT_THROW(f.value(), eggs_t); }
TEST(WindowExecutor, basic) { ManualExecutor executor; // int -> Future<int> auto fn = [executor_ = &executor]( std::vector<int> input, size_t window_size, size_t expect) { auto res = reduce( window( executor_, input, [](int i) { return makeFuture(i); }, window_size), 0, [](int sum, const Try<int>& b) { return sum + *b; }); executor_->waitFor(res); EXPECT_EQ(expect, std::move(res).get()); }; { SCOPED_TRACE("2 in-flight at a time"); std::vector<int> input = {1, 2, 3}; fn(input, 2, 6); } { SCOPED_TRACE("4 in-flight at a time"); std::vector<int> input = {1, 2, 3}; fn(input, 4, 6); } { SCOPED_TRACE("empty input"); std::vector<int> input; fn(input, 1, 0); } { // int -> Future<Unit> auto res = reduce( window( &executor, std::vector<int>({1, 2, 3}), [](int /* i */) { return makeFuture(); }, 2), 0, [](int sum, const Try<Unit>& b) { EXPECT_TRUE(b.hasValue()); return sum + 1; }); executor.waitFor(res); EXPECT_EQ(3, std::move(res).get()); } { // string -> return Future<int> auto res = reduce( window( &executor, std::vector<std::string>{"1", "2", "3"}, [](std::string s) { return makeFuture<int>(folly::to<int>(s)); }, 2), 0, [](int sum, const Try<int>& b) { return sum + *b; }); executor.waitFor(res); EXPECT_EQ(6, std::move(res).get()); } }
TEST(ManualExecutor, runIsStable) { ManualExecutor x; size_t count = 0; auto f1 = [&]() { count++; }; auto f2 = [&]() { x.add(f1); x.add(f1); }; x.add(f2); x.run(); }
TEST(ManualExecutor, advanceTo) { ManualExecutor x; size_t count = 0; x.scheduleAt([&]{ count++; }, std::chrono::steady_clock::now()); EXPECT_EQ(count, 0); x.advanceTo(std::chrono::steady_clock::now()); EXPECT_EQ(count, 1); }
TEST(ManualExecutor, scheduleAbs) { ManualExecutor x; size_t count = 0; x.scheduleAt([&]{ count++; }, x.now() + std::chrono::milliseconds(10)); EXPECT_EQ(count, 0); x.advance(std::chrono::milliseconds(10)); EXPECT_EQ(count, 1); }
TEST(ManualExecutor, advanceBack) { ManualExecutor x; size_t count = 0; x.advance(microseconds(5)); x.schedule([&]{ count++; }, microseconds(6)); EXPECT_EQ(count, 0); x.advanceTo(x.now() - microseconds(1)); EXPECT_EQ(count, 0); }
TEST(ViaFunc, liftsVoid) { ManualExecutor x; int count = 0; Future<Unit> f = via(&x, [&]{ count++; }); EXPECT_EQ(0, count); x.run(); EXPECT_EQ(1, count); }
TEST(Via, then2Variadic) { struct Foo { bool a = false; void foo(Try<Unit>) { a = true; } }; Foo f; ManualExecutor x; makeFuture().then(&x, &Foo::foo, &f); EXPECT_FALSE(f.a); x.run(); EXPECT_TRUE(f.a); }
TEST(ManualExecutor, advanceNeg) { ManualExecutor x; size_t count = 0; x.advance(std::chrono::microseconds(5)); x.schedule([&]{ count++; }, std::chrono::microseconds(6)); EXPECT_EQ(count, 0); x.advance(std::chrono::microseconds(-1)); EXPECT_EQ(count, 0); }
TEST(Coro, Basic2) { ManualExecutor executor; auto future = via(&executor, taskVoid()); EXPECT_FALSE(future.await_ready()); executor.drive(); EXPECT_TRUE(future.await_ready()); }
TEST(Coro, Basic) { ManualExecutor executor; auto future = via(&executor, task42()); EXPECT_FALSE(future.await_ready()); executor.drive(); EXPECT_TRUE(future.await_ready()); EXPECT_EQ(42, future.get()); }
TEST(Coro, Throw) { ManualExecutor executor; auto future = via(&executor, taskException()); EXPECT_FALSE(future.await_ready()); executor.drive(); EXPECT_TRUE(future.await_ready()); EXPECT_THROW(future.get(), std::runtime_error); }
TEST(ViaFunc, isSticky) { ManualExecutor x; int count = 0; auto f = via(&x, [&]{ count++; }); x.run(); f.then([&]{ count++; }); EXPECT_EQ(1, count); x.run(); EXPECT_EQ(2, count); }
TEST(ManualExecutor, scheduleDur) { ManualExecutor x; size_t count = 0; std::chrono::milliseconds dur {10}; x.schedule([&]{ count++; }, dur); EXPECT_EQ(count, 0); x.run(); EXPECT_EQ(count, 0); x.advance(dur/2); EXPECT_EQ(count, 0); x.advance(dur/2); EXPECT_EQ(count, 1); }
TEST(Via, viaRaces) { ManualExecutor x; Promise<Unit> p; auto tid = std::this_thread::get_id(); bool done = false; std::thread t1([&] { p.getFuture() .via(&x) .then([&](Try<Unit>&&) { EXPECT_EQ(tid, std::this_thread::get_id()); }) .then([&](Try<Unit>&&) { EXPECT_EQ(tid, std::this_thread::get_id()); }) .then([&](Try<Unit>&&) { done = true; }); }); std::thread t2([&] { p.setValue(); }); while (!done) x.run(); t1.join(); t2.join(); }
TEST(ManualExecutor, clockStartsAt0) { ManualExecutor x; EXPECT_EQ(x.now(), x.now().min()); }