Skip to content

feat(zest): support custom test fixtures#83

Merged
16bit-ykiko merged 4 commits intomainfrom
feat/custom-test-fixture
Mar 29, 2026
Merged

feat(zest): support custom test fixtures#83
16bit-ykiko merged 4 commits intomainfrom
feat/custom-test-fixture

Conversation

@16bit-ykiko
Copy link
Copy Markdown
Member

@16bit-ykiko 16bit-ykiko commented Mar 29, 2026

Summary

  • Refactor test state from TestSuiteDef member variable to thread-local, so assertion macros (EXPECT_*, ASSERT_*, CO_ASSERT_*) work anywhere — fixture methods, free functions, lambdas
  • Add TEST_SUITE_F(name, fixture) macro for inheriting from a custom fixture base class
  • failure() / pass() / skip() are now free functions in eventide::zest namespace

Test plan

  • All 674 existing tests pass with no changes
  • Add a test using TEST_SUITE_F with a custom fixture that calls assertions in fixture methods

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Add support for test suites that use a shared loop fixture and a unified scheduling helper.
    • Expose per-thread test outcome controls so tests can mark pass/fail/skip externally.
  • Refactor

    • Test outcome tracking moved from per-instance storage to thread-local state.
  • Bug Fixes

    • Restored correct early-skip behavior for the affected Windows test.

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) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 29, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 36aa78a8-0a46-430b-951f-b0fe0aba2c37

📥 Commits

Reviewing files that changed from the base of the PR and between d798b86 and abc6843.

📒 Files selected for processing (11)
  • include/eventide/zest/macro.h
  • tests/unit/async/build_system_tests.cpp
  • tests/unit/async/cancellation_tests.cpp
  • tests/unit/async/fs_tests.cpp
  • tests/unit/async/loop_fixture.h
  • tests/unit/async/process_tests.cpp
  • tests/unit/async/stream_tests.cpp
  • tests/unit/async/sync_tests.cpp
  • tests/unit/async/udp_tests.cpp
  • tests/unit/async/watcher_tests.cpp
  • tests/unit/async/work_request_tests.cpp
🚧 Files skipped from review as they are similar to previous changes (2)
  • include/eventide/zest/macro.h
  • tests/unit/async/process_tests.cpp

📝 Walkthrough

Walkthrough

Introduces thread-local test-state accessors and mutators; removes per-instance test state and mutators from TestSuiteDef; makes TEST_SUITE variadic and qualifies check failure calls; adds a loop_fixture with schedule_all and updates many async unit tests to use the fixture instead of per-test event_loop management.

Changes

Cohort / File(s) Summary
Thread‑Local State
include/eventide/zest/detail/registry.h
Added current_test_state() returning a thread-local TestState& initialized to Passed, plus helpers failure(), pass(), and skip() that set the thread-local state.
TestSuite Definition
include/eventide/zest/detail/suite.h
Removed TestSuiteDef's instance state member and its failure(), pass(), skip() methods; test runner now initializes current_test_state() and returns that thread-local state after running tests.
Macros & Checks
include/eventide/zest/macro.h
Changed TEST_SUITE(name) to accept variadic args (TEST_SUITE(name, ...)) and updated ZEST_CHECK_IMPL to call ::eventide::zest::failure() (fully qualified).
Loop Fixture
tests/unit/async/loop_fixture.h
Added eventide::loop_fixture that owns an event_loop and provides template<typename... Tasks> void schedule_all(Tasks&... tasks) to schedule tasks and run the loop.
Async Test Updates
tests/unit/async/...
tests/unit/async/process_tests.cpp, build_system_tests.cpp, cancellation_tests.cpp, fs_tests.cpp, stream_tests.cpp, sync_tests.cpp, udp_tests.cpp, watcher_tests.cpp, work_request_tests.cpp
Converted many async test suites to use TEST_SUITE(..., loop_fixture), removed per-test event_loop variables and manual loop.schedule(...); loop.run(); sequences, replaced with schedule_all(...) calls; removed eventide/async/async.h includes and added loop_fixture.h. One Windows test updated to call ::eventide::zest::skip().

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I hop through threads with a careful cheer,

Each test keeps its state, no baggage near,
Fixtures gather tasks, then run them all,
Passed, failed, or skipped — I celebrate the call,
Crunching carrots, one small clean hop, sincere.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.30% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: refactoring the zest test framework to support custom test fixtures through a new TEST_SUITE(name, fixture) macro form and thread-local test state management.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/custom-test-fixture

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
include/eventide/zest/macro.h (1)

25-30: ⚠️ Potential issue | 🟠 Major

ASSERT_* and CO_ASSERT_* in helper methods and setup() don't stop test execution.

These macros now compile in setup(), fixture helpers, and other non-test functions because failure() is fully qualified. However, they still only execute a plain return/co_return to ZEST_CHECK_IMPL, which exits that function but allows execution to continue. A failed assertion in setup() will set the test state to Failed and return from setup(), but run_test then proceeds directly to the test body. The test continues running despite the failure marker, breaking the intended fatal-assertion contract. To actually stop test execution early, a non-local unwind mechanism (exception, longjmp, etc.) would be needed instead of a scoped return.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@include/eventide/zest/macro.h` around lines 25 - 30, The macros
(ZEST_CHECK_IMPL used by ASSERT_* and CO_ASSERT_*) currently only execute a
local return_action, which lets setup() or helpers return and allows run_test to
continue; to fix, make the failure path perform a non-local unwind: implement a
dedicated exception type (e.g., ::eventide::zest::test_failure_exception) and
change ::eventide::zest::failure() to throw that exception (or add a new
::eventide::zest::abort_test() that throws), update ZEST_CHECK_IMPL to call the
throwing failure/abort helper instead of relying on return_action, and ensure
run_test catches this exception to stop executing the test body immediately;
target symbols: ZEST_CHECK_IMPL, ASSERT_*/CO_ASSERT_*,
::eventide::zest::failure(), run_test, setup().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@include/eventide/zest/macro.h`:
- Around line 25-30: The macros (ZEST_CHECK_IMPL used by ASSERT_* and
CO_ASSERT_*) currently only execute a local return_action, which lets setup() or
helpers return and allows run_test to continue; to fix, make the failure path
perform a non-local unwind: implement a dedicated exception type (e.g.,
::eventide::zest::test_failure_exception) and change ::eventide::zest::failure()
to throw that exception (or add a new ::eventide::zest::abort_test() that
throws), update ZEST_CHECK_IMPL to call the throwing failure/abort helper
instead of relying on return_action, and ensure run_test catches this exception
to stop executing the test body immediately; target symbols: ZEST_CHECK_IMPL,
ASSERT_*/CO_ASSERT_*, ::eventide::zest::failure(), run_test, setup().

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 292a4f23-72a8-4d6f-b204-c95c16653bd4

📥 Commits

Reviewing files that changed from the base of the PR and between 74aa776 and 17e3b13.

📒 Files selected for processing (3)
  • include/eventide/zest/detail/registry.h
  • include/eventide/zest/detail/suite.h
  • include/eventide/zest/macro.h

16bit-ykiko and others added 2 commits March 30, 2026 00:22
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
@16bit-ykiko 16bit-ykiko merged commit 2aa17d2 into main Mar 29, 2026
32 checks passed
@16bit-ykiko 16bit-ykiko deleted the feat/custom-test-fixture branch March 29, 2026 16:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant