#include #include #include #include "test_util.h" #include "uvco/combinators.h" #include "uvco/exception.h" #include "uvco/loop/loop.h" #include "uvco/promise/multipromise.h" #include "uvco/promise/promise.h" #include "uvco/run.h" #include "uvco/util.h" #include #include #include namespace { using namespace uvco; TEST(MultiPromiseTest, standardGenerator) { constexpr static int countMax = 10; auto generator = []() -> uvco::MultiPromise { for (int i = 0; i < countMax; ++i) { co_yield i; } }; auto setup = [&generator](const Loop &) -> uvco::Promise { MultiPromise ticker = generator(); for (int i = 0; i < countMax; ++i) { const auto value = co_await ticker; EXPECT_TRUE(value.has_value()); EXPECT_EQ(i, value.value()); } EXPECT_EQ(co_await ticker, std::nullopt); EXPECT_EQ(co_await ticker, std::nullopt); }; run_loop(setup); } TEST(MultiPromiseTest, generatorThrows) { constexpr static int countMax = 3; auto generator = []() -> uvco::MultiPromise { for (int i = 0; i < countMax; ++i) { co_yield i; } throw UvcoException("ticker"); }; auto setup = [&generator](const Loop &) -> uvco::Promise { MultiPromise ticker = generator(); for (int i = 0; i < countMax; ++i) { const auto value = co_await ticker; EXPECT_TRUE(value.has_value()); EXPECT_EQ(i, value.value()); } // Repeatedly throws. EXPECT_THROW({ co_await ticker; }, UvcoException); EXPECT_THROW({ co_await ticker; }, UvcoException); }; run_loop(setup); } MultiPromise miniTicker() { for (int i = 0; i < 3; ++i) { co_yield i; co_await yield(); } throw UvcoException("ticker"); } TEST(MultiPromiseTest, exceptionWithTimer) { auto setup = [](const Loop &) -> uvco::Promise { MultiPromise ticker = miniTicker(); EXPECT_EQ(co_await ticker, 0); EXPECT_EQ(co_await ticker, 1); EXPECT_EQ(co_await ticker, 2); EXPECT_THROW({ co_await ticker; }, UvcoException); }; run_loop(setup); } TEST(MultiPromiseTest, yield) { auto setup = [](const Loop &) -> uvco::Promise { static constexpr unsigned count = 10; MultiPromise ticker = yield(count); for (unsigned i = 0; i < count; ++i) { EXPECT_EQ(co_await ticker, i); } }; run_loop(setup); } TEST(MultiPromiseTest, nextValue) { auto setup = [](const Loop &) -> uvco::Promise { MultiPromise gen = []() -> uvco::MultiPromise { co_yield 1; co_yield 2; co_yield 3; }(); EXPECT_EQ(co_await gen.next(), std::optional{1}); EXPECT_EQ(co_await gen.next(), std::optional{2}); EXPECT_EQ(co_await gen.next(), std::optional{3}); EXPECT_EQ(co_await gen.next(), std::nullopt); }; run_loop(setup); } TEST(MultiPromiseTest, cancel) { bool cancelled = false; bool finished = false; auto setup = [&cancelled, &finished](const Loop &) -> uvco::Promise { MultiPromise gen = [&cancelled, &finished]() -> uvco::MultiPromise { const OnExit onExit{[&] { cancelled = true; }}; co_yield 1; co_yield 2; co_yield 3; finished = true; }(); EXPECT_EQ(co_await gen, 1); gen.cancel(); EXPECT_EQ(std::nullopt, co_await gen); }; run_loop(setup); EXPECT_FALSE(finished); EXPECT_TRUE(cancelled); } TEST(MultiPromiseTest, drop) { bool cancelled = false; bool finished = false; auto setup = [&cancelled, &finished](const Loop &) -> uvco::Promise { { MultiPromise gen = [&cancelled, &finished]() -> uvco::MultiPromise { const OnExit onExit{[&] { cancelled = true; }}; co_yield 1; co_yield 2; co_yield 3; finished = true; }(); EXPECT_EQ(co_await gen, 1); } // gen is dropped here }; run_loop(setup); EXPECT_FALSE(finished); EXPECT_TRUE(cancelled); } TEST(MultiPromiseTest, DISABLED_benchmarkYield) { static constexpr unsigned count = 1'000'000; auto setup = [](const Loop &) -> uvco::Promise { MultiPromise ticker = yield(count); for (unsigned i = 0; i < count; ++i) { EXPECT_EQ(co_await ticker, i); } }; run_loop(setup); } TEST(MultiPromiseTest, DISABLED_benchmarkYieldNext) { static constexpr unsigned count = 1'000'000; auto setup = [](const Loop &) -> uvco::Promise { MultiPromise ticker = yield(count); for (unsigned i = 0; i < count; ++i) { EXPECT_EQ((co_await ticker.next()).value(), i); } }; run_loop(setup); } } // namespace