#include "uvco/channel.h" #include "uvco/exception.h" #include "uvco/promise/multipromise.h" #include "uvco/promise/promise.h" #include "uvco/run.h" #include "test_util.h" #include #include namespace { using namespace uvco; } TEST(BQTest, basicPushPop) { BoundedQueue bque{4}; EXPECT_EQ(bque.size(), 0); EXPECT_TRUE(bque.empty()); bque.put(1); bque.put(2); EXPECT_EQ(bque.size(), 2); EXPECT_EQ(bque.get(), 1); bque.put(3); bque.put(4); EXPECT_EQ(bque.size(), 3); EXPECT_EQ(bque.get(), 2); bque.put(5); bque.put(6); EXPECT_EQ(bque.size(), 4); EXPECT_EQ(bque.get(), 3); bque.put(7); EXPECT_EQ(bque.size(), 4); EXPECT_EQ(bque.get(), 4); } TEST(BQTest, pushTooMany) { BoundedQueue bque(2); bque.put(1); bque.put(2); // Only dies in CMAKE_BUILD_TYPE=Debug. EXPECT_THROW({ bque.put(3); }, UvcoException); } TEST(BQTest, popEmpty) { BoundedQueue bque(2); bque.put(1); bque.put(2); EXPECT_EQ(bque.get(), 1); EXPECT_EQ(bque.get(), 2); EXPECT_EQ(bque.size(), 0); // Only dies in CMAKE_BUILD_TYPE=Debug. EXPECT_THROW({ bque.get(); }, UvcoException); } TEST(ChannelTest, basicWriteRead) { auto setup = [&](const Loop &) -> Promise { Channel chan{3}; co_await chan.put(1); co_await chan.put(2); EXPECT_EQ(co_await chan.get(), 1); }; run_loop(setup); } // On my out-dated Core i5-7300U @ 2.60GHz, this results in about 130 ns per // item. TEST(ChannelTest, DISABLED_basicWriteReadBench) { static constexpr int N_iter = 1000000; auto reader = [](Channel &chan) -> Promise { for (int i = 1; i < N_iter; ++i) { EXPECT_EQ(co_await chan.get(), i); } }; auto writer = [](Channel &chan) -> Promise { for (int i = 1; i < N_iter; ++i) { co_await chan.put(i); } }; auto setup = [&](const Loop &) -> Promise { Channel chan{2}; Promise readerCoroutine = reader(chan); Promise writerCoroutine = writer(chan); co_await readerCoroutine; co_await writerCoroutine; }; run_loop(setup); } TEST(ChannelTest, DISABLED_generatorWriteReadBench) { static constexpr int N_iter = 1000000; auto reader = [](Channel &chan) -> Promise { MultiPromise gen = chan.getAll(); for (int i = 1; i < N_iter; ++i) { EXPECT_EQ(i, co_await gen); } }; auto writer = [](Channel &chan) -> Promise { for (int i = 1; i < N_iter; ++i) { co_await chan.put(i); } }; auto setup = [&](const Loop &) -> Promise { Channel chan{2}; Promise readerCoroutine = reader(chan); Promise writerCoroutine = writer(chan); co_await readerCoroutine; co_await writerCoroutine; }; run_loop(setup); } TEST(ChannelTest, blockingRead) { auto drain = [](Channel &chan) -> Promise { for (int i = 1; i < 3; ++i) { EXPECT_EQ(co_await chan.get(), i); } }; bool reachedEnd = false; auto setup = [&](const Loop &) -> Promise { Channel chan{3}; Promise drainer = drain(chan); Promise put1 = chan.put(1); Promise put1New{std::move(put1)}; co_await put1New; co_await chan.put(2); co_await chan.put(3); co_await chan.put(4); EXPECT_EQ(co_await chan.get(), 3); EXPECT_EQ(co_await chan.get(), 4); co_await drainer; reachedEnd = true; }; run_loop(setup); // May not be the case if a co_await stalled: then run_loop finishes // prematurely. EXPECT_TRUE(reachedEnd); } TEST(ChannelTest, blockingWriteBench) { auto source = [](Channel &chan, int numIters) -> Promise { for (int i = 1; i < numIters + 1; ++i) { co_await chan.put(i); } }; bool reachedEnd = false; auto setup = [&](const Loop &) -> Promise { Channel chan{2}; constexpr static int N_iter = 10; Promise sourcer = source(chan, N_iter); for (int i = 1; i < N_iter; ++i) { EXPECT_EQ(co_await chan.get(), i); } co_await sourcer; reachedEnd = true; }; run_loop(setup); // May not be the case if a co_await stalled: then run_loop finishes // prematurely. EXPECT_TRUE(reachedEnd); } TEST(ChannelTest, cancelRead) { auto setup = [&](const Loop &) -> Promise { Channel chan{2}; { Promise reader = chan.get(); } chan.put(123); EXPECT_EQ(123, co_await chan.get()); co_return; }; run_loop(setup); } TEST(ChannelTest, cancelWrite) { auto setup = [&](const Loop &) -> Promise { Channel chan{1}; auto writer1 = chan.put(1); EXPECT_TRUE(writer1.ready()); co_await writer1; { Promise writer = chan.put(2); EXPECT_FALSE(writer.ready()); } EXPECT_EQ(1, co_await chan.get()); auto writer2 = chan.put(3); EXPECT_TRUE(writer2.ready()); co_await writer2; EXPECT_EQ(3, co_await chan.get()); co_return; }; run_loop(setup); } TEST(ChannelTest, multipleWaiters) { auto reader = [](Channel &chan) -> Promise { co_await chan.get(); }; bool reachedEnd = false; auto setup = [&](const Loop &) -> Promise { Channel chan{2}; Promise prom1 = reader(chan); Promise prom2 = reader(chan); co_await chan.put(1); co_await chan.put(2); co_await prom1; co_await prom2; reachedEnd = true; }; run_loop(setup); // May not be the case if a co_await stalled: then run_loop finishes // prematurely. EXPECT_TRUE(reachedEnd); } TEST(ChannelTest, tooManyWaiters) { auto reader = [](Channel &chan) -> Promise { try { co_await chan.get(); } catch (const UvcoException &e) { throw e; } }; bool reachedEnd = false; auto setup = [&](const Loop &) -> Promise { Channel chan{2, 1}; Promise prom1 = reader(chan); Promise prom2 = reader(chan); co_await chan.put(1); co_await prom1; EXPECT_THROW({ co_await prom2; }, UvcoException); reachedEnd = true; }; run_loop(setup); // May not be the case if a co_await stalled: then run_loop finishes // prematurely. EXPECT_TRUE(reachedEnd); } TEST(ChannelTest, channelGenerator) { auto writer = [](Channel &chan, int numIters) -> Promise { for (int i = 0; i < numIters; ++i) { co_await chan.put(i); } }; constexpr static int numIters = 10; int counter = 0; auto setup = [&](const Loop &) -> Promise { Channel chan{2}; MultiPromise gen = chan.getAll(); Promise writerCoroutine = writer(chan, numIters); for (int i = 0; i < numIters; ++i) { EXPECT_EQ(counter = (co_await gen).value(), i); } }; run_loop(setup); EXPECT_EQ(counter, numIters - 1); }