From 17e3b13cd2b4cc1c454cf2f618fff19cdf23c48c Mon Sep 17 00:00:00 2001 From: ykiko Date: Mon, 30 Mar 2026 00:08:17 +0800 Subject: [PATCH 1/4] feat(zest): support custom test fixtures via thread-local state Refactor test state from member variable to thread-local, making assertion macros usable anywhere (fixture methods, free functions, lambdas). Add TEST_SUITE_F macro for custom fixture base classes. Co-Authored-By: Claude Opus 4.6 (1M context) --- include/eventide/zest/detail/registry.h | 17 +++++++++++++++++ include/eventide/zest/detail/suite.h | 19 ++----------------- include/eventide/zest/macro.h | 5 ++++- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/include/eventide/zest/detail/registry.h b/include/eventide/zest/detail/registry.h index 3b6ee5a6..ca1c921f 100644 --- a/include/eventide/zest/detail/registry.h +++ b/include/eventide/zest/detail/registry.h @@ -34,6 +34,23 @@ struct TestSuite { std::vector (*cases)(); }; +inline TestState& current_test_state() { + thread_local TestState state = TestState::Passed; + return state; +} + +inline void failure() { + current_test_state() = TestState::Failed; +} + +inline void pass() { + current_test_state() = TestState::Passed; +} + +inline void skip() { + current_test_state() = TestState::Skipped; +} + class Runner { public: static Runner& instance(); diff --git a/include/eventide/zest/detail/suite.h b/include/eventide/zest/detail/suite.h index b0048783..ee32c45d 100644 --- a/include/eventide/zest/detail/suite.h +++ b/include/eventide/zest/detail/suite.h @@ -7,24 +7,8 @@ namespace eventide::zest { template struct TestSuiteDef { -private: - TestState state = TestState::Passed; - -public: using Self = Derived; - void failure() { - state = TestState::Failed; - } - - void pass() { - state = TestState::Passed; - } - - void skip() { - state = TestState::Skipped; - } - constexpr inline static auto& test_cases() { static std::vector instance; return instance; @@ -47,6 +31,7 @@ struct TestSuiteDef { TestAttrs attrs = {}> inline static bool _register_test_case = [] { auto run_test = +[] -> TestState { + current_test_state() = TestState::Passed; Derived test; if constexpr(requires { test.setup(); }) { test.setup(); @@ -58,7 +43,7 @@ struct TestSuiteDef { test.teardown(); } - return test.state; + return current_test_state(); }; test_cases().emplace_back(case_name.data(), path.data(), line, attrs, run_test); diff --git a/include/eventide/zest/macro.h b/include/eventide/zest/macro.h index 1de43248..43004ef5 100644 --- a/include/eventide/zest/macro.h +++ b/include/eventide/zest/macro.h @@ -6,6 +6,9 @@ #define TEST_SUITE(name) struct name##TEST : ::eventide::zest::TestSuiteDef<#name, name##TEST> +#define TEST_SUITE_F(name, fixture) \ + struct name##TEST : fixture, ::eventide::zest::TestSuiteDef<#name, name##TEST> + #define TEST_CASE(name, ...) \ void _register_##name() { \ constexpr auto file_name = std::source_location::current().file_name(); \ @@ -23,7 +26,7 @@ do { \ if(condition) [[unlikely]] { \ ::eventide::zest::print_trace(std::source_location::current()); \ - failure(); \ + ::eventide::zest::failure(); \ return_action; \ } \ } while(0) From d798b86e165d3c091493679b6cc0cd440cb416a0 Mon Sep 17 00:00:00 2001 From: ykiko Date: Mon, 30 Mar 2026 00:18:45 +0800 Subject: [PATCH 2/4] fix(zest): use fully qualified call for skip() in process tests Co-Authored-By: Claude Opus 4.6 (1M context) --- tests/unit/async/process_tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/async/process_tests.cpp b/tests/unit/async/process_tests.cpp index 3be18589..a3317eb5 100644 --- a/tests/unit/async/process_tests.cpp +++ b/tests/unit/async/process_tests.cpp @@ -220,7 +220,7 @@ TEST_CASE(spawn_pipe_stderr) { TEST_CASE(spawn_pipe_stdout_read_chunk_twice) { #ifdef _WIN32 - skip(); + ::eventide::zest::skip(); return; #else event_loop loop; From b47489bc687a756d2aa90dee19fe40e9a8ef6bde Mon Sep 17 00:00:00 2001 From: ykiko Date: Mon, 30 Mar 2026 00:22:32 +0800 Subject: [PATCH 3/4] refactor(zest): merge TEST_SUITE_F into TEST_SUITE via __VA_OPT__ TEST_SUITE(name) for default, TEST_SUITE(name, fixture) for custom fixture base class. No need for a separate macro. Co-Authored-By: Claude Opus 4.6 (1M context) --- include/eventide/zest/macro.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/include/eventide/zest/macro.h b/include/eventide/zest/macro.h index 43004ef5..2c4e9284 100644 --- a/include/eventide/zest/macro.h +++ b/include/eventide/zest/macro.h @@ -4,10 +4,8 @@ #include "eventide/zest/detail/suite.h" #include "eventide/zest/detail/trace.h" -#define TEST_SUITE(name) struct name##TEST : ::eventide::zest::TestSuiteDef<#name, name##TEST> - -#define TEST_SUITE_F(name, fixture) \ - struct name##TEST : fixture, ::eventide::zest::TestSuiteDef<#name, name##TEST> +#define TEST_SUITE(name, ...) \ + struct name##TEST : __VA_OPT__(__VA_ARGS__, )::eventide::zest::TestSuiteDef<#name, name##TEST> #define TEST_CASE(name, ...) \ void _register_##name() { \ From abc684338b552f96cc072876ec87d14c4bda3cc4 Mon Sep 17 00:00:00 2001 From: ykiko Date: Mon, 30 Mar 2026 00:51:56 +0800 Subject: [PATCH 4/4] refactor(async): use loop_fixture to eliminate event_loop boilerplate Extract shared loop_fixture with schedule_all() helper to simplify async test setup across 9 test files (-296 lines, +85 lines). Co-Authored-By: Claude Opus 4.6 (1M context) --- tests/unit/async/build_system_tests.cpp | 35 +++--------- tests/unit/async/cancellation_tests.cpp | 76 +++++-------------------- tests/unit/async/fs_tests.cpp | 39 +++---------- tests/unit/async/loop_fixture.h | 17 ++++++ tests/unit/async/process_tests.cpp | 47 ++++----------- tests/unit/async/stream_tests.cpp | 67 +++++----------------- tests/unit/async/sync_tests.cpp | 51 ++++------------- tests/unit/async/udp_tests.cpp | 18 ++---- tests/unit/async/watcher_tests.cpp | 34 +++-------- tests/unit/async/work_request_tests.cpp | 14 ++--- 10 files changed, 102 insertions(+), 296 deletions(-) create mode 100644 tests/unit/async/loop_fixture.h diff --git a/tests/unit/async/build_system_tests.cpp b/tests/unit/async/build_system_tests.cpp index 6ea55a6d..fac113ad 100644 --- a/tests/unit/async/build_system_tests.cpp +++ b/tests/unit/async/build_system_tests.cpp @@ -1,8 +1,8 @@ #include #include "compile_graph.h" +#include "loop_fixture.h" #include "eventide/zest/zest.h" -#include "eventide/async/async.h" namespace eventide { @@ -26,10 +26,9 @@ static CompileGraph make_test_graph() { return graph; } -TEST_SUITE(build_system) { +TEST_SUITE(build_system, loop_fixture) { TEST_CASE(normal_compilation_completes) { - event_loop loop; auto graph = make_test_graph(); auto test = [&]() -> task<> { @@ -39,12 +38,10 @@ TEST_CASE(normal_compilation_completes) { }; auto t = test(); - loop.schedule(t); - loop.run(); + schedule_all(t); } TEST_CASE(update_cancels_in_flight) { - event_loop loop; auto graph = make_test_graph(); bool compile_cancelled = false; @@ -60,16 +57,12 @@ TEST_CASE(update_cancels_in_flight) { auto c = compiler(); auto u = updater(); - - loop.schedule(c); - loop.schedule(u); - loop.run(); + schedule_all(c, u); EXPECT_TRUE(compile_cancelled); } TEST_CASE(chain_cancel_propagates) { - event_loop loop; auto graph = make_test_graph(); bool compile_cancelled = false; @@ -85,16 +78,12 @@ TEST_CASE(chain_cancel_propagates) { auto c = compiler(); auto u = updater(); - - loop.schedule(c); - loop.schedule(u); - loop.run(); + schedule_all(c, u); EXPECT_TRUE(compile_cancelled); } TEST_CASE(recompile_after_update) { - event_loop loop; auto graph = make_test_graph(); auto test = [&]() -> task<> { @@ -109,12 +98,10 @@ TEST_CASE(recompile_after_update) { }; auto t = test(); - loop.schedule(t); - loop.run(); + schedule_all(t); } TEST_CASE(independent_compilations_unaffected) { - event_loop loop; auto graph = make_test_graph(); bool parser_cancelled = false; bool codegen_ok = false; @@ -137,18 +124,13 @@ TEST_CASE(independent_compilations_unaffected) { auto cp = compile_parser(); auto cc = compile_codegen(); auto u = updater(); - - loop.schedule(cp); - loop.schedule(cc); - loop.schedule(u); - loop.run(); + schedule_all(cp, cc, u); EXPECT_TRUE(parser_cancelled); EXPECT_TRUE(codegen_ok); } TEST_CASE(shared_dependency_compiled_once) { - event_loop loop; int compile_count = 0; // Use a side-effecting delay_fn to count actual compilations @@ -168,8 +150,7 @@ TEST_CASE(shared_dependency_compiled_once) { }; auto t = test(); - loop.schedule(t); - loop.run(); + schedule_all(t); // common.h (1) + a.cpp (1) + b.cpp (1) = 3 // Without dedup this would be 4 (common.h compiled twice). diff --git a/tests/unit/async/cancellation_tests.cpp b/tests/unit/async/cancellation_tests.cpp index 88087f75..2189a00e 100644 --- a/tests/unit/async/cancellation_tests.cpp +++ b/tests/unit/async/cancellation_tests.cpp @@ -6,8 +6,8 @@ #include #include +#include "loop_fixture.h" #include "eventide/zest/zest.h" -#include "eventide/async/async.h" namespace eventide { @@ -37,7 +37,7 @@ int uv_thread_pool_size_for_test() { return value; } -TEST_SUITE(cancellation) { +TEST_SUITE(cancellation, loop_fixture) { TEST_CASE(pass_through_value) { cancellation_source source; @@ -67,7 +67,6 @@ TEST_CASE(pre_cancel_skip) { } TEST_CASE(cancel_in_flight) { - event_loop loop; cancellation_source source; event gate; int started = 0; @@ -93,11 +92,7 @@ TEST_CASE(cancel_in_flight) { auto guarded_task = with_token(worker(), source.token()); auto cancel_task = canceler(); auto release_task = releaser(); - - loop.schedule(guarded_task); - loop.schedule(cancel_task); - loop.schedule(release_task); - loop.run(); + schedule_all(guarded_task, cancel_task, release_task); auto result = guarded_task.value(); EXPECT_FALSE(result.has_value()); @@ -127,7 +122,6 @@ TEST_CASE(token_share_state) { } TEST_CASE(queue_cancel_resume) { - event_loop loop; cancellation_source source; event start_target; event target_submitted; @@ -214,7 +208,6 @@ TEST_CASE(queue_cancel_resume) { } TEST_CASE(fs_cancel_resume) { - event_loop loop; cancellation_source source; event start_target; event target_submitted; @@ -295,7 +288,6 @@ TEST_CASE(fs_cancel_resume) { } TEST_CASE(cancel_waiting_on_event) { - event_loop loop; cancellation_source source; event gate; bool started = false; @@ -315,10 +307,7 @@ TEST_CASE(cancel_waiting_on_event) { auto guarded = with_token(worker(), source.token()); auto cancel_task = canceler(); - - loop.schedule(guarded); - loop.schedule(cancel_task); - loop.run(); + schedule_all(guarded, cancel_task); EXPECT_TRUE(started); EXPECT_FALSE(finished); @@ -354,7 +343,6 @@ TEST_CASE(wait_sync_primitive) { } TEST_CASE(cancel_waiting_on_mutex) { - event_loop loop; cancellation_source source; mutex m; bool started = false; @@ -382,11 +370,7 @@ TEST_CASE(cancel_waiting_on_mutex) { auto holder_task = holder(); auto guarded = with_token(worker(), source.token()); auto cancel_task = canceler(); - - loop.schedule(holder_task); - loop.schedule(guarded); - loop.schedule(cancel_task); - loop.run(); + schedule_all(holder_task, guarded, cancel_task); EXPECT_TRUE(started); EXPECT_FALSE(acquired); @@ -398,7 +382,6 @@ TEST_CASE(cancel_waiting_on_mutex) { } TEST_CASE(cancel_semaphore_waiter) { - event_loop loop; cancellation_source source; semaphore sem(0); bool started = false; @@ -418,10 +401,7 @@ TEST_CASE(cancel_semaphore_waiter) { auto guarded = with_token(worker(), source.token()); auto cancel_task = canceler(); - - loop.schedule(guarded); - loop.schedule(cancel_task); - loop.run(); + schedule_all(guarded, cancel_task); EXPECT_TRUE(started); EXPECT_FALSE(acquired); @@ -433,7 +413,6 @@ TEST_CASE(cancel_semaphore_waiter) { } TEST_CASE(cancel_condition_variable_waiter) { - event_loop loop; cancellation_source source; mutex m; condition_variable cv; @@ -456,10 +435,7 @@ TEST_CASE(cancel_condition_variable_waiter) { auto guarded = with_token(worker(), source.token()); auto cancel_task = canceler(); - - loop.schedule(guarded); - loop.schedule(cancel_task); - loop.run(); + schedule_all(guarded, cancel_task); EXPECT_TRUE(started); EXPECT_FALSE(notified); @@ -467,7 +443,6 @@ TEST_CASE(cancel_condition_variable_waiter) { } TEST_CASE(cancel_multiple_registered_tasks) { - event_loop loop; cancellation_source source; event gate1, gate2, gate3; int started = 0; @@ -490,12 +465,7 @@ TEST_CASE(cancel_multiple_registered_tasks) { auto g2 = with_token(make_worker(gate2), token); auto g3 = with_token(make_worker(gate3), token); auto cancel_task = canceler(); - - loop.schedule(g1); - loop.schedule(g2); - loop.schedule(g3); - loop.schedule(cancel_task); - loop.run(); + schedule_all(g1, g2, g3, cancel_task); EXPECT_EQ(started, 3); EXPECT_EQ(finished, 0); @@ -507,7 +477,6 @@ TEST_CASE(cancel_multiple_registered_tasks) { TEST_CASE(nested_with_token) { // (a) Cancel outer -> entire chain cancelled { - event_loop loop; cancellation_source outer_source; cancellation_source inner_source; event gate; @@ -524,17 +493,13 @@ TEST_CASE(nested_with_token) { auto guarded = with_token(with_token(worker(), inner_source.token()), outer_source.token()); auto cancel_task = canceler(); - - loop.schedule(guarded); - loop.schedule(cancel_task); - loop.run(); + schedule_all(guarded, cancel_task); EXPECT_FALSE(guarded.value().has_value()); } // (b) Cancel inner -> inner task reports cancellation, outer observes it { - event_loop loop; cancellation_source outer_source; cancellation_source inner_source; event gate; @@ -551,10 +516,7 @@ TEST_CASE(nested_with_token) { auto guarded = with_token(with_token(worker(), inner_source.token()), outer_source.token()); auto cancel_task = canceler(); - - loop.schedule(guarded); - loop.schedule(cancel_task); - loop.run(); + schedule_all(guarded, cancel_task); // Outer observes cancellation result from inner EXPECT_FALSE(guarded.value().has_value()); @@ -583,7 +545,6 @@ TEST_CASE(token_reuse_after_cancel) { } TEST_CASE(multi_token_cancel_first) { - event_loop loop; cancellation_source source1; cancellation_source source2; event gate; @@ -602,10 +563,7 @@ TEST_CASE(multi_token_cancel_first) { auto guarded = with_token(worker(), source1.token(), source2.token()); auto cancel_task = canceler(); - - loop.schedule(guarded); - loop.schedule(cancel_task); - loop.run(); + schedule_all(guarded, cancel_task); EXPECT_FALSE(finished); EXPECT_FALSE(guarded.value().has_value()); @@ -614,7 +572,6 @@ TEST_CASE(multi_token_cancel_first) { } TEST_CASE(multi_token_cancel_second) { - event_loop loop; cancellation_source source1; cancellation_source source2; event gate; @@ -633,10 +590,7 @@ TEST_CASE(multi_token_cancel_second) { auto guarded = with_token(worker(), source1.token(), source2.token()); auto cancel_task = canceler(); - - loop.schedule(guarded); - loop.schedule(cancel_task); - loop.run(); + schedule_all(guarded, cancel_task); EXPECT_FALSE(finished); EXPECT_FALSE(guarded.value().has_value()); @@ -674,7 +628,6 @@ TEST_CASE(multi_token_pass_through) { } TEST_CASE(nested_with_token_same_token_cancel) { - event_loop loop; cancellation_source source; auto token = source.token(); event gate; @@ -697,9 +650,8 @@ TEST_CASE(nested_with_token_same_token_cancel) { co_return; }; - loop.schedule(guarded); - loop.schedule(canceler()); - EXPECT_EQ(loop.run(), 0); + auto cancel_task = canceler(); + schedule_all(guarded, cancel_task); } }; // TEST_SUITE(cancellation) diff --git a/tests/unit/async/fs_tests.cpp b/tests/unit/async/fs_tests.cpp index 32306007..b7a2ab68 100644 --- a/tests/unit/async/fs_tests.cpp +++ b/tests/unit/async/fs_tests.cpp @@ -9,9 +9,9 @@ #include #endif +#include "loop_fixture.h" #include "../common/fd_helpers.h" #include "eventide/zest/zest.h" -#include "eventide/async/async.h" namespace eventide { @@ -103,14 +103,11 @@ task mkstemp_roundtrip(event_loop& loop) { } // namespace -TEST_SUITE(fs_request_io) { +TEST_SUITE(fs_request_io, loop_fixture) { TEST_CASE(basic_roundtrip) { - event_loop loop; - auto worker = fs_roundtrip(loop); - loop.schedule(worker); - loop.run(); + schedule_all(worker); auto result = worker.result(); EXPECT_TRUE(result.has_value()); @@ -118,11 +115,8 @@ TEST_CASE(basic_roundtrip) { } TEST_CASE(mkstemp_and_access) { - event_loop loop; - auto worker = mkstemp_roundtrip(loop); - loop.schedule(worker); - loop.run(); + schedule_all(worker); auto result = worker.result(); EXPECT_TRUE(result.has_value()); @@ -130,8 +124,6 @@ TEST_CASE(mkstemp_and_access) { } TEST_CASE(async_open_read_write_close) { - event_loop loop; - auto worker = [](event_loop& loop) -> task { auto dir_template = (std::filesystem::temp_directory_path() / "eventide-rw-XXXXXX").string(); @@ -163,8 +155,7 @@ TEST_CASE(async_open_read_write_close) { co_return got == payload ? 1 : 0; }(loop); - loop.schedule(worker); - loop.run(); + schedule_all(worker); auto result = worker.result(); EXPECT_TRUE(result.has_value()); @@ -174,8 +165,6 @@ TEST_CASE(async_open_read_write_close) { #ifndef _WIN32 TEST_CASE(symlink_readlink_realpath) { - event_loop loop; - auto worker = [](event_loop& loop) -> task { auto dir_template = (std::filesystem::temp_directory_path() / "eventide-sym-XXXXXX").string(); @@ -214,8 +203,7 @@ TEST_CASE(symlink_readlink_realpath) { co_return is_link ? 1 : 0; }(loop); - loop.schedule(worker); - loop.run(); + schedule_all(worker); auto result = worker.result(); EXPECT_TRUE(result.has_value()); @@ -223,8 +211,6 @@ TEST_CASE(symlink_readlink_realpath) { } TEST_CASE(chown_fchown_lchown) { - event_loop loop; - auto worker = [](event_loop& loop) -> task { auto dir_template = (std::filesystem::temp_directory_path() / "eventide-chown-XXXXXX").string(); @@ -262,8 +248,7 @@ TEST_CASE(chown_fchown_lchown) { co_return 1; }(loop); - loop.schedule(worker); - loop.run(); + schedule_all(worker); auto result = worker.result(); EXPECT_TRUE(result.has_value()); @@ -271,8 +256,6 @@ TEST_CASE(chown_fchown_lchown) { } TEST_CASE(fchmod) { - event_loop loop; - auto worker = [](event_loop& loop) -> task { auto dir_template = (std::filesystem::temp_directory_path() / "eventide-fchmod-XXXXXX").string(); @@ -292,8 +275,7 @@ TEST_CASE(fchmod) { co_return mode_ok ? 1 : 0; }(loop); - loop.schedule(worker); - loop.run(); + schedule_all(worker); auto result = worker.result(); EXPECT_TRUE(result.has_value()); @@ -303,8 +285,6 @@ TEST_CASE(fchmod) { #endif // !_WIN32 TEST_CASE(statfs_basic) { - event_loop loop; - auto worker = [](event_loop& loop) -> task { auto statfs_path = std::filesystem::temp_directory_path().string(); auto stats = co_await fs::statfs(statfs_path, loop).or_fail(); @@ -312,8 +292,7 @@ TEST_CASE(statfs_basic) { co_return stats.bsize > 0 ? 1 : 0; }(loop); - loop.schedule(worker); - loop.run(); + schedule_all(worker); auto result = worker.result(); EXPECT_TRUE(result.has_value()); diff --git a/tests/unit/async/loop_fixture.h b/tests/unit/async/loop_fixture.h new file mode 100644 index 00000000..f3f9ca66 --- /dev/null +++ b/tests/unit/async/loop_fixture.h @@ -0,0 +1,17 @@ +#pragma once + +#include "eventide/async/async.h" + +namespace eventide { + +struct loop_fixture { + event_loop loop; + + template + void schedule_all(Tasks&... tasks) { + (loop.schedule(tasks), ...); + loop.run(); + } +}; + +} // namespace eventide diff --git a/tests/unit/async/process_tests.cpp b/tests/unit/async/process_tests.cpp index a3317eb5..2b466487 100644 --- a/tests/unit/async/process_tests.cpp +++ b/tests/unit/async/process_tests.cpp @@ -3,8 +3,8 @@ #include #include +#include "loop_fixture.h" #include "eventide/zest/zest.h" -#include "eventide/async/async.h" namespace eventide { @@ -50,11 +50,9 @@ task, result>> read_two_chunks(pipe p } // namespace -TEST_SUITE(process_io) { +TEST_SUITE(process_io, loop_fixture) { TEST_CASE(spawn_wait_simple) { - event_loop loop; - process::options opts; #ifdef _WIN32 opts.file = "cmd.exe"; @@ -71,9 +69,7 @@ TEST_CASE(spawn_wait_simple) { EXPECT_TRUE(spawn_res->proc.pid() > 0); auto worker = wait_for_exit(spawn_res->proc); - - loop.schedule(worker); - loop.run(); + schedule_all(worker); auto status = worker.result(); EXPECT_TRUE(status.has_value()); @@ -82,8 +78,6 @@ TEST_CASE(spawn_wait_simple) { } TEST_CASE(spawn_pipe_stdout) { - event_loop loop; - process::options opts; #ifdef _WIN32 opts.file = "cmd.exe"; @@ -118,13 +112,11 @@ TEST_CASE(spawn_pipe_stdout) { event_loop::current().stop(); }; - loop.schedule(capture_stdout()); - loop.run(); + auto t = capture_stdout(); + schedule_all(t); } TEST_CASE(spawn_pipe_stdio) { - event_loop loop; - process::options opts; #ifdef _WIN32 opts.file = "cmd.exe"; @@ -172,13 +164,11 @@ TEST_CASE(spawn_pipe_stdio) { event_loop::current().stop(); }; - loop.schedule(write_stdin_capture_stdout()); - loop.run(); + auto t = write_stdin_capture_stdout(); + schedule_all(t); } TEST_CASE(spawn_pipe_stderr) { - event_loop loop; - process::options opts; #ifdef _WIN32 opts.file = "cmd.exe"; @@ -214,8 +204,8 @@ TEST_CASE(spawn_pipe_stderr) { event_loop::current().stop(); }; - loop.schedule(capture_stdout_stderr()); - loop.run(); + auto t = capture_stdout_stderr(); + schedule_all(t); } TEST_CASE(spawn_pipe_stdout_read_chunk_twice) { @@ -223,8 +213,6 @@ TEST_CASE(spawn_pipe_stdout_read_chunk_twice) { ::eventide::zest::skip(); return; #else - event_loop loop; - process::options opts; opts.file = "/bin/sh"; opts.args = {opts.file, "-c", "printf 'chunk-one'; sleep 0.05; printf 'chunk-two'"}; @@ -236,8 +224,7 @@ TEST_CASE(spawn_pipe_stdout_read_chunk_twice) { ASSERT_TRUE(spawn_res.has_value()); auto reader = read_two_chunks(std::move(spawn_res->stdout_pipe)); - loop.schedule(reader); - loop.run(); + schedule_all(reader); auto [first, second] = reader.result(); ASSERT_TRUE(first.has_value()); @@ -248,8 +235,6 @@ TEST_CASE(spawn_pipe_stdout_read_chunk_twice) { } TEST_CASE(spawn_invalid_file) { - event_loop loop; - process::options opts; #ifdef _WIN32 opts.file = "Z:\\nonexistent\\eventide-nope.exe"; @@ -262,8 +247,6 @@ TEST_CASE(spawn_invalid_file) { } TEST_CASE(wait_twice) { - event_loop loop; - process::options opts; #ifdef _WIN32 opts.file = "cmd.exe"; @@ -280,10 +263,7 @@ TEST_CASE(wait_twice) { int done = 0; auto first = wait_for_exit(spawn_res->proc, done, 2); auto second = wait_for_exit(spawn_res->proc, done, 2); - - loop.schedule(first); - loop.schedule(second); - loop.run(); + schedule_all(first, second); auto first_result = first.result(); auto second_result = second.result(); @@ -307,8 +287,6 @@ TEST_CASE(query_info_self) { } TEST_CASE(query_info_child) { - event_loop loop; - process::options opts; #ifdef _WIN32 opts.file = "cmd.exe"; @@ -338,8 +316,7 @@ TEST_CASE(query_info_child) { EXPECT_EQ(info2->pid, pid); auto worker = wait_for_exit(spawn_res->proc); - loop.schedule(worker); - loop.run(); + schedule_all(worker); } TEST_CASE(query_info_invalid_pid) { diff --git a/tests/unit/async/stream_tests.cpp b/tests/unit/async/stream_tests.cpp index e90b6f0a..56938817 100644 --- a/tests/unit/async/stream_tests.cpp +++ b/tests/unit/async/stream_tests.cpp @@ -4,8 +4,8 @@ #include #include +#include "loop_fixture.h" #include "eventide/zest/macro.h" -#include "eventide/async/async.h" #ifdef _WIN32 #include @@ -295,7 +295,7 @@ int set_abortive_close(socket_t sock) { } // namespace -TEST_SUITE(pipe) { +TEST_SUITE(pipe, loop_fixture) { TEST_CASE(read_from_fd) { int fds[2] = {-1, -1}; @@ -306,14 +306,11 @@ TEST_CASE(read_from_fd) { static_cast(message.size())); close_fd(fds[1]); - event_loop loop; auto pipe_res = pipe::open(fds[0], {}, loop); ASSERT_TRUE(pipe_res.has_value()); auto reader = read_from_pipe(std::move(*pipe_res)); - - loop.schedule(reader); - loop.run(); + schedule_all(reader); auto result = reader.result(); EXPECT_TRUE(result.has_value()); @@ -331,14 +328,11 @@ TEST_CASE(read_some_fd) { static_cast(message.size())); close_fd(fds[1]); - event_loop loop; auto pipe_res = pipe::open(fds[0], {}, loop); ASSERT_TRUE(pipe_res.has_value()); auto reader = read_some_from_pipe(std::move(*pipe_res)); - - loop.schedule(reader); - loop.run(); + schedule_all(reader); auto result = reader.result(); EXPECT_TRUE(result.has_value()); @@ -356,14 +350,11 @@ TEST_CASE(read_chunk_fd) { static_cast(message.size())); close_fd(fds[1]); - event_loop loop; auto pipe_res = pipe::open(fds[0], {}, loop); ASSERT_TRUE(pipe_res.has_value()); auto reader = read_chunk_from_pipe(std::move(*pipe_res)); - - loop.schedule(reader); - loop.run(); + schedule_all(reader); auto result = reader.result(); EXPECT_EQ(result.first, message); @@ -374,17 +365,13 @@ TEST_CASE(read_chunk_then_read_some_fd) { int fds[2] = {-1, -1}; ASSERT_EQ(create_pipe(fds), 0); - event_loop loop; event first_chunk_consumed; auto pipe_res = pipe::open(fds[0], {}, loop); ASSERT_TRUE(pipe_res.has_value()); auto reader = read_chunk_then_some(std::move(*pipe_res), first_chunk_consumed); auto writer = write_two_pipe_chunks(fds[1], loop, first_chunk_consumed); - - loop.schedule(reader); - loop.schedule(writer); - loop.run(); + schedule_all(reader, writer); auto [first, second] = reader.result(); ASSERT_TRUE(first.has_value()); @@ -394,8 +381,6 @@ TEST_CASE(read_chunk_then_read_some_fd) { } TEST_CASE(connect_and_accept) { - event_loop loop; - #ifdef _WIN32 const std::string name = "\\\\.\\pipe\\eventide-test-pipe"; #else @@ -415,10 +400,7 @@ TEST_CASE(connect_and_accept) { const std::string message = "eventide-pipe-connect"; auto server = accept_and_read_pipe(std::move(*acc_res), done); auto client = connect_and_write_pipe(name, message, done); - - loop.schedule(server); - loop.schedule(client); - loop.run(); + schedule_all(server, client); auto server_res = server.result(); auto client_res = client.result(); @@ -431,8 +413,6 @@ TEST_CASE(connect_and_accept) { } TEST_CASE(connect_failure) { - event_loop loop; - #ifdef _WIN32 const std::string name = "\\\\.\\pipe\\eventide-test-pipe-missing"; #else @@ -445,17 +425,13 @@ TEST_CASE(connect_failure) { int done = 0; auto client = connect_pipe(name, done, 1); - - loop.schedule(client); - loop.run(); + schedule_all(client); auto client_res = client.result(); EXPECT_FALSE(client_res.has_value()); } TEST_CASE(stop) { - event_loop loop; - #ifdef _WIN32 const std::string name = "\\\\.\\pipe\\eventide-test-pipe-missing"; #else @@ -480,8 +456,7 @@ TEST_CASE(stop) { co_return res; }(*acc); - loop.schedule(task1); - loop.run(); + schedule_all(task1); auto res1 = task1.value().value(); EXPECT_TRUE(!res1.has_value() && res1.error() == error::operation_aborted); @@ -492,8 +467,7 @@ TEST_CASE(stop) { co_return res; }(*acc); - loop.schedule(task2); - loop.run(); + schedule_all(task2); EXPECT_TRUE(!task2->is_finished()); acc->stop(); @@ -502,13 +476,12 @@ TEST_CASE(stop) { }; // TEST_SUITE(pipe) -TEST_SUITE(tcp) { +TEST_SUITE(tcp, loop_fixture) { TEST_CASE(accept_and_read) { int port = pick_free_port(); ASSERT_TRUE(port > 0); - event_loop loop; auto acc_res = tcp::listen("127.0.0.1", port, {}, loop); ASSERT_TRUE(acc_res.has_value()); @@ -529,8 +502,7 @@ TEST_CASE(accept_and_read) { static_cast(message.size())); close_socket(client_fd); - loop.schedule(server); - loop.run(); + schedule_all(server); auto result = server.result(); EXPECT_TRUE(result.has_value()); @@ -541,7 +513,6 @@ TEST_CASE(accept_already_waiting) { int port = pick_free_port(); ASSERT_TRUE(port > 0); - event_loop loop; auto acc_res = tcp::listen("127.0.0.1", port, {}, loop); ASSERT_TRUE(acc_res.has_value()); @@ -562,9 +533,7 @@ TEST_CASE(accept_already_waiting) { ASSERT_EQ(::connect(client_fd, reinterpret_cast(&addr), sizeof(addr)), 0); close_socket(client_fd); - loop.schedule(first); - loop.schedule(second); - loop.run(); + schedule_all(first, second); auto first_res = first.result(); auto second_res = second.result(); @@ -579,17 +548,13 @@ TEST_CASE(connect_and_write) { int port = pick_free_port(); ASSERT_TRUE(port > 0); - event_loop loop; auto acc_res = tcp::listen("127.0.0.1", port, {}, loop); ASSERT_TRUE(acc_res.has_value()); int done = 0; auto server = accept_and_read_once(std::move(*acc_res), done); auto client = connect_and_send("127.0.0.1", port, "eventide-tcp-connect", done); - - loop.schedule(server); - loop.schedule(client); - loop.run(); + schedule_all(server, client); auto server_res = server.result(); auto client_res = client.result(); @@ -602,7 +567,6 @@ TEST_CASE(read_some_error) { int port = pick_free_port(); ASSERT_TRUE(port > 0); - event_loop loop; auto acc_res = tcp::listen("127.0.0.1", port, {}, loop); ASSERT_TRUE(acc_res.has_value()); @@ -620,8 +584,7 @@ TEST_CASE(read_some_error) { ASSERT_EQ(set_abortive_close(client_fd), 0); close_socket(client_fd); - loop.schedule(server); - loop.run(); + schedule_all(server); auto result = server.result(); EXPECT_FALSE(result.has_value()); diff --git a/tests/unit/async/sync_tests.cpp b/tests/unit/async/sync_tests.cpp index ce86daf2..ef2bdca5 100644 --- a/tests/unit/async/sync_tests.cpp +++ b/tests/unit/async/sync_tests.cpp @@ -1,7 +1,7 @@ #include +#include "loop_fixture.h" #include "eventide/zest/zest.h" -#include "eventide/async/async.h" namespace eventide { @@ -9,7 +9,7 @@ namespace { using namespace std::chrono; -TEST_SUITE(sync) { +TEST_SUITE(sync, loop_fixture) { TEST_CASE(mutex_try_lock) { mutex m; @@ -21,7 +21,6 @@ TEST_CASE(mutex_try_lock) { } TEST_CASE(mutex_lock_order) { - event_loop loop; mutex m; int step = 0; @@ -44,15 +43,12 @@ TEST_CASE(mutex_lock_order) { auto t1 = holder(); auto t2 = waiter(); - loop.schedule(t1); - loop.schedule(t2); - loop.run(); + schedule_all(t1, t2); EXPECT_EQ(step, 2); } TEST_CASE(event_set_wait) { - event_loop loop; event ev; int fired = 0; @@ -69,15 +65,12 @@ TEST_CASE(event_set_wait) { auto t1 = waiter(); auto t2 = setter(); - loop.schedule(t1); - loop.schedule(t2); - loop.run(); + schedule_all(t1, t2); EXPECT_EQ(fired, 1); } TEST_CASE(manual_reset_all) { - event_loop loop; event ev(true); int count = 0; @@ -91,15 +84,12 @@ TEST_CASE(manual_reset_all) { auto t1 = waiter(); auto t2 = waiter(); - loop.schedule(t1); - loop.schedule(t2); - loop.run(); + schedule_all(t1, t2); EXPECT_EQ(count, 2); } TEST_CASE(event_interrupt) { - event_loop loop; event ev; bool reached = false; @@ -122,13 +112,10 @@ TEST_CASE(event_interrupt) { auto t1 = driver(); auto t2 = intr(); - loop.schedule(t1); - loop.schedule(t2); - loop.run(); + schedule_all(t1, t2); } TEST_CASE(interrupt_many) { - event_loop loop; event ev; int cancelled = 0; @@ -149,16 +136,12 @@ TEST_CASE(interrupt_many) { auto t1 = waiter(); auto t2 = waiter(); auto t3 = intr(); - loop.schedule(t1); - loop.schedule(t2); - loop.schedule(t3); - loop.run(); + schedule_all(t1, t2, t3); EXPECT_EQ(cancelled, 2); } TEST_CASE(interrupt_snapshot) { - event_loop loop; event ev; int cancelled = 0; bool second_wait_cancelled = false; @@ -186,17 +169,13 @@ TEST_CASE(interrupt_snapshot) { auto t1 = waiter(); auto t2 = intr(); auto t3 = setter(); - loop.schedule(t1); - loop.schedule(t2); - loop.schedule(t3); - loop.run(); + schedule_all(t1, t2, t3); EXPECT_EQ(cancelled, 1); EXPECT_FALSE(second_wait_cancelled); } TEST_CASE(future_wait) { - event_loop loop; event ev; bool fired = false; @@ -216,9 +195,7 @@ TEST_CASE(future_wait) { auto t1 = waiter(); auto t2 = setter(); - loop.schedule(t1); - loop.schedule(t2); - loop.run(); + schedule_all(t1, t2); EXPECT_TRUE(fired); } @@ -236,7 +213,6 @@ TEST_CASE(signal_state) { } TEST_CASE(semaphore_acquire_release) { - event_loop loop; semaphore sem(1); int step = 0; @@ -258,15 +234,12 @@ TEST_CASE(semaphore_acquire_release) { auto t1 = first(); auto t2 = second(); - loop.schedule(t1); - loop.schedule(t2); - loop.run(); + schedule_all(t1, t2); EXPECT_EQ(step, 2); } TEST_CASE(condition_variable_wait) { - event_loop loop; mutex m; condition_variable cv; bool ready = false; @@ -294,9 +267,7 @@ TEST_CASE(condition_variable_wait) { auto t1 = waiter(); auto t2 = notifier(); - loop.schedule(t1); - loop.schedule(t2); - loop.run(); + schedule_all(t1, t2); EXPECT_EQ(step, 3); } diff --git a/tests/unit/async/udp_tests.cpp b/tests/unit/async/udp_tests.cpp index db2a304d..ce0a7b29 100644 --- a/tests/unit/async/udp_tests.cpp +++ b/tests/unit/async/udp_tests.cpp @@ -1,8 +1,8 @@ #include #include +#include "loop_fixture.h" #include "eventide/zest/zest.h" -#include "eventide/async/async.h" namespace eventide { @@ -40,11 +40,9 @@ task send_connected(udp& sock, std::string_view payload, int& done) } // namespace -TEST_SUITE(udp_io) { +TEST_SUITE(udp_io, loop_fixture) { TEST_CASE(send_and_recv) { - event_loop loop; - auto recv_sock = udp::create(loop); ASSERT_TRUE(recv_sock.has_value()); @@ -60,10 +58,7 @@ TEST_CASE(send_and_recv) { int done = 0; auto receiver = recv_once(*recv_sock, done); auto sender = send_to(*send_sock, "eventide-udp", endpoint->addr, endpoint->port, done); - - loop.schedule(receiver); - loop.schedule(sender); - loop.run(); + schedule_all(receiver, sender); auto recv_result = receiver.result(); EXPECT_TRUE(recv_result.has_value()); @@ -74,8 +69,6 @@ TEST_CASE(send_and_recv) { } TEST_CASE(connect_and_send) { - event_loop loop; - auto recv_sock = udp::create(loop); ASSERT_TRUE(recv_sock.has_value()); @@ -94,10 +87,7 @@ TEST_CASE(connect_and_send) { int done = 0; auto receiver = recv_once(*recv_sock, done); auto sender = send_connected(*send_sock, "eventide-udp-connect", done); - - loop.schedule(receiver); - loop.schedule(sender); - loop.run(); + schedule_all(receiver, sender); auto recv_result = receiver.result(); EXPECT_TRUE(recv_result.has_value()); diff --git a/tests/unit/async/watcher_tests.cpp b/tests/unit/async/watcher_tests.cpp index 91b9f579..4aee83c7 100644 --- a/tests/unit/async/watcher_tests.cpp +++ b/tests/unit/async/watcher_tests.cpp @@ -1,7 +1,7 @@ #include +#include "loop_fixture.h" #include "eventide/zest/zest.h" -#include "eventide/async/async.h" namespace eventide { @@ -47,73 +47,55 @@ task<> wait_timer_twice(timer& t) { } // namespace -TEST_SUITE(watcher_io) { +TEST_SUITE(watcher_io, loop_fixture) { TEST_CASE(timer_wait) { - event_loop loop; - auto t = timer::create(loop); t.start(std::chrono::milliseconds{1}, std::chrono::milliseconds{0}); auto waiter = wait_timer(t); - loop.schedule(waiter); - loop.run(); + schedule_all(waiter); } TEST_CASE(idle_wait) { - event_loop loop; - auto w = idle::create(loop); w.start(); auto waiter = wait_idle(w); - loop.schedule(waiter); - loop.run(); + schedule_all(waiter); w.stop(); } TEST_CASE(sleep_once) { - event_loop loop; - auto sleeper = wait_sleep(loop); - loop.schedule(sleeper); - loop.run(); + schedule_all(sleeper); } TEST_CASE(timer_repeat_twice) { - event_loop loop; - auto t = timer::create(loop); t.start(std::chrono::milliseconds{1}, std::chrono::milliseconds{1}); auto waiter = wait_timer_twice(t); - loop.schedule(waiter); - loop.run(); + schedule_all(waiter); } TEST_CASE(prepare_wait) { - event_loop loop; - auto w = prepare::create(loop); w.start(); auto waiter = wait_prepare(w); - loop.schedule(waiter); - loop.run(); + schedule_all(waiter); w.stop(); } TEST_CASE(check_wait) { - event_loop loop; - auto w = check::create(loop); w.start(); auto waiter = wait_check(w); - loop.schedule(waiter); - loop.run(); + schedule_all(waiter); w.stop(); } diff --git a/tests/unit/async/work_request_tests.cpp b/tests/unit/async/work_request_tests.cpp index e5371c7d..401e2e8a 100644 --- a/tests/unit/async/work_request_tests.cpp +++ b/tests/unit/async/work_request_tests.cpp @@ -1,7 +1,7 @@ #include +#include "loop_fixture.h" #include "eventide/zest/zest.h" -#include "eventide/async/async.h" namespace eventide { @@ -24,15 +24,13 @@ task } // namespace -TEST_SUITE(work_request_io) { +TEST_SUITE(work_request_io, loop_fixture) { TEST_CASE(queue_runs) { - event_loop loop; std::atomic flag{0}; auto worker = wait_work(flag, loop); - loop.schedule(worker); - loop.run(); + schedule_all(worker); auto ec = worker.result(); EXPECT_FALSE(ec.has_error()); @@ -40,16 +38,12 @@ TEST_CASE(queue_runs) { } TEST_CASE(queue_runs_twice) { - event_loop loop; std::atomic flag{0}; std::atomic done{0}; auto first = wait_work_target(flag, done, 2, loop); auto second = wait_work_target(flag, done, 2, loop); - - loop.schedule(first); - loop.schedule(second); - loop.run(); + schedule_all(first, second); auto ec1 = first.result(); auto ec2 = second.result();