uvco/test/async_work_test.cc
2025-11-17 21:42:14 +01:00

187 lines
4 KiB
C++

#include <fcntl.h>
#include <fmt/core.h>
#include <fmt/ranges.h>
#include <gtest/gtest.h>
#include <uv.h>
#include "test_util.h"
#include "uvco/async_work.h"
#include "uvco/loop/loop.h"
#include "uvco/promise/promise.h"
#include "uvco/timer.h"
#include <atomic>
#include <functional>
#include <stdexcept>
#include <thread>
#include <vector>
namespace {
using namespace uvco;
TEST(AsyncWorkTest, basicVoidFunction) {
bool ran = false;
auto work = [&]() -> void { ran = true; };
auto setup = [&](const Loop &loop) -> Promise<void> {
co_await submitWork<void>(loop, work);
};
run_loop(setup);
EXPECT_TRUE(ran);
}
TEST(AsyncWorkTest, scheduleMany) {
static constexpr unsigned num = 100;
std::atomic<unsigned> counter = 0;
std::vector<unsigned> order;
order.resize(num);
auto work = [&](unsigned) -> void { ++counter; };
auto setup = [&](const Loop &loop) -> Promise<void> {
std::vector<Promise<void>> promises;
for (unsigned i = 0; i < num; ++i) {
promises.push_back(submitWork<void>(loop, [i, &work]() { work(i); }));
}
unsigned waited{};
for (auto &p : promises) {
co_await p;
++waited;
}
EXPECT_EQ(waited, num);
};
run_loop(setup);
EXPECT_EQ(counter, num);
}
TEST(AsyncWorkTest, resultReturned) {
static constexpr unsigned num = 100;
auto work = [](unsigned i) -> unsigned { return i; };
auto setup = [&](const Loop &loop) -> Promise<void> {
std::vector<Promise<unsigned>> promises;
for (unsigned i = 0; i < num; ++i) {
promises.push_back(
submitWork(loop, std::function<unsigned()>{
[i, &work]() -> unsigned { return work(i); }}));
}
unsigned waited{};
for (auto &p : promises) {
auto result = co_await p;
EXPECT_EQ(result, waited);
++waited;
}
EXPECT_EQ(waited, num);
};
run_loop(setup);
}
TEST(AsyncWorkTest, cancelled) {
auto work = []() -> void {
std::this_thread::sleep_for(std::chrono::milliseconds{10});
};
auto setup = [&](const Loop &loop) -> Promise<void> {
auto p = submitWork<void>(loop, work);
co_return;
};
run_loop(setup);
}
TEST(AsyncWorkTest, cancelledLate) {
std::atomic<bool> started;
auto work = [&started]() -> void {
started.store(true);
std::this_thread::sleep_for(std::chrono::milliseconds{10});
};
auto setup = [&](const Loop &loop) -> Promise<void> {
auto p = submitWork<void>(loop, work);
co_await sleep(loop, 1);
co_return;
};
run_loop(setup);
EXPECT_TRUE(started);
}
TEST(AsyncWorkTest, exceptionThrown) {
auto work = []() -> void { throw std::runtime_error("test"); };
auto setup = [&](const Loop &loop) -> Promise<void> {
auto p = submitWork<void>(loop, work);
try {
co_await p;
EXPECT_TRUE(false) << "Expected exception";
} catch (const std::runtime_error &e) {
EXPECT_STREQ(e.what(), "test");
}
};
run_loop(setup);
}
TEST(AsyncWorkTest, exceptionThrownForValue) {
auto work = []() -> unsigned { throw std::runtime_error("test"); };
auto setup = [&](const Loop &loop) -> Promise<void> {
auto p = submitWork<unsigned>(loop, work);
try {
co_await p;
EXPECT_TRUE(false) << "Expected exception";
} catch (const std::runtime_error &e) {
EXPECT_STREQ(e.what(), "test");
}
};
run_loop(setup);
}
TEST(ThreadLocalKeyTest, basicSingleThread) {
ThreadLocalKey<unsigned> key;
key.set(42);
EXPECT_EQ(key.get(), 42);
key.set(100);
EXPECT_EQ(key.get(), 100);
key.del();
EXPECT_NO_THROW({
key.set(7);
EXPECT_EQ(key.get(), 7);
});
}
TEST(ThreadLocalKeyTest, differentThreadsDifferentValues) {
ThreadLocalKey<unsigned> key;
auto setup = [&key](const Loop &loop) -> Promise<void> {
key.set(1);
EXPECT_EQ(key.get(), 1);
co_await submitWork<void>(loop, [&key]() {
key.set(2);
EXPECT_EQ(key.get(), 2);
key.del();
});
EXPECT_EQ(1, key.get());
co_return;
};
run_loop(setup);
key.del();
}
} // namespace