uvco/test/channel_test.cc
2025-11-13 22:38:28 +01:00

314 lines
6.7 KiB
C++

#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 <gtest/gtest.h>
#include <utility>
namespace {
using namespace uvco;
}
TEST(BQTest, basicPushPop) {
BoundedQueue<int> 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<int> 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<int> 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<void> {
Channel<int> 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<int> &chan) -> Promise<void> {
for (int i = 1; i < N_iter; ++i) {
EXPECT_EQ(co_await chan.get(), i);
}
};
auto writer = [](Channel<int> &chan) -> Promise<void> {
for (int i = 1; i < N_iter; ++i) {
co_await chan.put(i);
}
};
auto setup = [&](const Loop &) -> Promise<void> {
Channel<int> chan{2};
Promise<void> readerCoroutine = reader(chan);
Promise<void> 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<int> &chan) -> Promise<void> {
MultiPromise<int> gen = chan.getAll();
for (int i = 1; i < N_iter; ++i) {
EXPECT_EQ(i, co_await gen);
}
};
auto writer = [](Channel<int> &chan) -> Promise<void> {
for (int i = 1; i < N_iter; ++i) {
co_await chan.put(i);
}
};
auto setup = [&](const Loop &) -> Promise<void> {
Channel<int> chan{2};
Promise<void> readerCoroutine = reader(chan);
Promise<void> writerCoroutine = writer(chan);
co_await readerCoroutine;
co_await writerCoroutine;
};
run_loop(setup);
}
TEST(ChannelTest, blockingRead) {
auto drain = [](Channel<int> &chan) -> Promise<void> {
for (int i = 1; i < 3; ++i) {
EXPECT_EQ(co_await chan.get(), i);
}
};
bool reachedEnd = false;
auto setup = [&](const Loop &) -> Promise<void> {
Channel<int> chan{3};
Promise<void> drainer = drain(chan);
Promise<void> put1 = chan.put(1);
Promise<void> 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<int> &chan, int numIters) -> Promise<void> {
for (int i = 1; i < numIters + 1; ++i) {
co_await chan.put(i);
}
};
bool reachedEnd = false;
auto setup = [&](const Loop &) -> Promise<void> {
Channel<int> chan{2};
constexpr static int N_iter = 10;
Promise<void> 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<void> {
Channel<int> chan{2};
{
Promise<int> 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<void> {
Channel<int> chan{1};
auto writer1 = chan.put(1);
EXPECT_TRUE(writer1.ready());
co_await writer1;
{
Promise<void> 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<int> &chan) -> Promise<void> {
co_await chan.get();
};
bool reachedEnd = false;
auto setup = [&](const Loop &) -> Promise<void> {
Channel<int> chan{2};
Promise<void> prom1 = reader(chan);
Promise<void> 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<int> &chan) -> Promise<void> {
try {
co_await chan.get();
} catch (const UvcoException &e) {
throw e;
}
};
bool reachedEnd = false;
auto setup = [&](const Loop &) -> Promise<void> {
Channel<int> chan{2, 1};
Promise<void> prom1 = reader(chan);
Promise<void> 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<int> &chan, int numIters) -> Promise<void> {
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<void> {
Channel<int> chan{2};
MultiPromise<int> gen = chan.getAll();
Promise<void> 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);
}