305 lines
6.4 KiB
C++
305 lines
6.4 KiB
C++
#include "../lib/Core.hpp"
|
|
#include <cassert>
|
|
#include <iostream>
|
|
#include <thread>
|
|
#include <chrono>
|
|
#include <sys/eventfd.h>
|
|
#include <unistd.h>
|
|
#include <atomic>
|
|
#include <vector>
|
|
|
|
class TestEventLoop
|
|
{
|
|
private:
|
|
std::unique_ptr<reactor::EventLoop> loop_;
|
|
std::thread thread_;
|
|
|
|
public:
|
|
TestEventLoop()
|
|
{
|
|
thread_ = std::thread([this]() {
|
|
loop_ = std::make_unique<reactor::EventLoop>();
|
|
loop_->loop();
|
|
});
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
}
|
|
|
|
~TestEventLoop()
|
|
{
|
|
if (loop_) {
|
|
loop_->quit();
|
|
}
|
|
if (thread_.joinable()) {
|
|
thread_.join();
|
|
}
|
|
}
|
|
|
|
reactor::EventLoop* getLoop() { return loop_.get(); }
|
|
};
|
|
|
|
void test_timer_basic()
|
|
{
|
|
std::cout << "Testing basic timer functionality...\n";
|
|
|
|
TestEventLoop test_loop;
|
|
auto loop = test_loop.getLoop();
|
|
|
|
bool timer_fired = false;
|
|
[[maybe_unused]] auto timer_id = loop->runAfter(reactor::Duration(50), [&timer_fired]() {
|
|
timer_fired = true;
|
|
});
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
assert(timer_fired);
|
|
|
|
std::cout << "✓ Basic timer passed\n";
|
|
}
|
|
|
|
void test_timer_cancellation()
|
|
{
|
|
std::cout << "Testing timer cancellation...\n";
|
|
|
|
TestEventLoop test_loop;
|
|
auto loop = test_loop.getLoop();
|
|
|
|
bool timer_fired = false;
|
|
auto timer_id = loop->runAfter(reactor::Duration(100), [&timer_fired]() {
|
|
timer_fired = true;
|
|
});
|
|
|
|
loop->cancel(timer_id);
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(150));
|
|
assert(!timer_fired);
|
|
|
|
std::cout << "✓ Timer cancellation passed\n";
|
|
}
|
|
|
|
void test_repeating_timer()
|
|
{
|
|
std::cout << "Testing repeating timer...\n";
|
|
|
|
TestEventLoop test_loop;
|
|
auto loop = test_loop.getLoop();
|
|
|
|
int count = 0;
|
|
auto timer_id = loop->runEvery(reactor::Duration(20), [&count]() {
|
|
count++;
|
|
});
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(85));
|
|
loop->cancel(timer_id);
|
|
|
|
assert(count >= 3 && count <= 5);
|
|
std::cout << "✓ Repeating timer passed (count: " << count << ")\n";
|
|
}
|
|
|
|
void test_run_in_loop()
|
|
{
|
|
std::cout << "Testing runInLoop functionality...\n";
|
|
|
|
TestEventLoop test_loop;
|
|
auto loop = test_loop.getLoop();
|
|
|
|
bool task_executed = false;
|
|
loop->runInLoop([&task_executed]() {
|
|
task_executed = true;
|
|
});
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
|
assert(task_executed);
|
|
|
|
std::cout << "✓ runInLoop passed\n";
|
|
}
|
|
|
|
void test_queue_in_loop()
|
|
{
|
|
std::cout << "Testing queueInLoop functionality...\n";
|
|
|
|
TestEventLoop test_loop;
|
|
auto loop = test_loop.getLoop();
|
|
|
|
std::vector<int> execution_order;
|
|
|
|
loop->queueInLoop([&execution_order]() {
|
|
execution_order.push_back(1);
|
|
});
|
|
|
|
loop->queueInLoop([&execution_order]() {
|
|
execution_order.push_back(2);
|
|
});
|
|
|
|
loop->runInLoop([&execution_order]() {
|
|
execution_order.push_back(3);
|
|
});
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
|
|
|
assert(execution_order.size() == 3);
|
|
assert(execution_order[0] == 1 || execution_order[0] == 2 || execution_order[0] == 3);
|
|
|
|
|
|
std::cout << "✓ queueInLoop passed\n";
|
|
}
|
|
|
|
void test_channel_basic()
|
|
{
|
|
std::cout << "Testing basic channel functionality...\n";
|
|
|
|
TestEventLoop test_loop;
|
|
auto loop = test_loop.getLoop();
|
|
|
|
int event_fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
|
|
assert(event_fd >= 0);
|
|
|
|
auto channel = std::make_unique<reactor::Channel>(loop, event_fd);
|
|
|
|
bool read_callback_called = false;
|
|
channel->setReadCallback([&read_callback_called]() {
|
|
read_callback_called = true;
|
|
});
|
|
|
|
channel->enableReading();
|
|
|
|
uint64_t val = 1;
|
|
write(event_fd, &val, sizeof(val));
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
|
|
|
assert(read_callback_called);
|
|
|
|
channel->disableAll();
|
|
channel->remove();
|
|
close(event_fd);
|
|
|
|
std::cout << "✓ Basic channel passed\n";
|
|
}
|
|
|
|
void test_channel_write_events()
|
|
{
|
|
std::cout << "Testing channel write events...\n";
|
|
|
|
TestEventLoop test_loop;
|
|
auto loop = test_loop.getLoop();
|
|
|
|
int event_fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
|
|
assert(event_fd >= 0);
|
|
|
|
auto channel = std::make_unique<reactor::Channel>(loop, event_fd);
|
|
|
|
bool write_callback_called = false;
|
|
channel->setWriteCallback([&write_callback_called]() {
|
|
write_callback_called = true;
|
|
});
|
|
|
|
channel->enableWriting();
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
|
|
|
assert(write_callback_called);
|
|
|
|
channel->disableAll();
|
|
channel->remove();
|
|
close(event_fd);
|
|
|
|
std::cout << "✓ Channel write events passed\n";
|
|
}
|
|
|
|
void test_multiple_timers()
|
|
{
|
|
std::cout << "Testing multiple timers...\n";
|
|
|
|
TestEventLoop test_loop;
|
|
auto loop = test_loop.getLoop();
|
|
|
|
std::vector<bool> timer_states(5, false);
|
|
|
|
for (int i = 0; i < 5; ++i) {
|
|
loop->runAfter(reactor::Duration(10 + i * 20), [&timer_states, i]() {
|
|
timer_states[i] = true;
|
|
});
|
|
}
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(120));
|
|
|
|
for (bool state : timer_states) {
|
|
assert(state);
|
|
}
|
|
|
|
std::cout << "✓ Multiple timers passed\n";
|
|
}
|
|
|
|
void test_timer_precision()
|
|
{
|
|
std::cout << "Testing timer precision...\n";
|
|
|
|
TestEventLoop test_loop;
|
|
auto loop = test_loop.getLoop();
|
|
|
|
auto start_time = std::chrono::steady_clock::now();
|
|
bool timer_fired = false;
|
|
|
|
loop->runAfter(reactor::Duration(100), [&timer_fired, start_time]() {
|
|
auto end_time = std::chrono::steady_clock::now();
|
|
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
|
|
timer_fired = true;
|
|
|
|
assert(duration.count() >= 90 && duration.count() <= 150);
|
|
});
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(150));
|
|
assert(timer_fired);
|
|
|
|
std::cout << "✓ Timer precision passed\n";
|
|
}
|
|
|
|
void test_event_loop_thread_safety()
|
|
{
|
|
std::cout << "Testing event loop thread safety...\n";
|
|
|
|
TestEventLoop test_loop;
|
|
auto loop = test_loop.getLoop();
|
|
|
|
std::atomic<int> counter{0};
|
|
constexpr int num_tasks = 100;
|
|
|
|
std::vector<std::thread> threads;
|
|
for (int t = 0; t < 4; ++t) {
|
|
threads.emplace_back([loop, &counter, num_tasks]() {
|
|
for (int i = 0; i < num_tasks; ++i) {
|
|
loop->queueInLoop([&counter]() {
|
|
counter++;
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
for (auto& thread : threads) {
|
|
thread.join();
|
|
}
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
assert(counter == 4 * num_tasks);
|
|
|
|
std::cout << "✓ Event loop thread safety passed\n";
|
|
}
|
|
|
|
int main()
|
|
{
|
|
std::cout << "=== Core Tests ===\n";
|
|
|
|
test_timer_basic();
|
|
test_timer_cancellation();
|
|
test_repeating_timer();
|
|
test_run_in_loop();
|
|
test_queue_in_loop();
|
|
test_channel_basic();
|
|
test_channel_write_events();
|
|
test_multiple_timers();
|
|
test_timer_precision();
|
|
test_event_loop_thread_safety();
|
|
|
|
std::cout << "All core tests passed! ✓\n";
|
|
return 0;
|
|
}
|