Skip to content

Include the test helper once in every test that loads it - #309

Merged
diolektor merged 1 commit into
mainfrom
chore/tests-require-once-sweep-test-helper
Aug 16, 2026
Merged

Include the test helper once in every test that loads it#309
diolektor merged 1 commit into
mainfrom
chore/tests-require-once-sweep-test-helper

Conversation

@diolektor

Copy link
Copy Markdown
Contributor

tests/php/test_helper.php declares class TestCase at file level. A persistent PHP worker keeps its class table between requests, so a test that pulls the helper in with a bare require fatals on the second request one worker serves:

Fatal error: Cannot redeclare class TestCase (previously declared in .../test_helper.php:5)

The runner sees a non-JSON body and reports HTTP 500 against whichever test drew that worker, so the failure names the wrong culprit and moves from run to run.

This replaces require with require_once in the 234 places that load the helper.

Not a fix for a live failure

Nothing currently running in a worker-mode profile still used the bare form — all 142 suite entries of the seven profiles that set WORKER_FILE (worker, hooks, hooksdb, fibers, breaker, revolt, revoltclassic) were already on require_once. What this closes is the trap: 234 files carried a form that would fatal the moment one of them is moved into a worker suite, which happens routinely, and would misreport when it did.

Three of the 234 live outside tests/php/ (fixtures/framework/index.php, fixtures/php_deny/public/index.php, fixtures/php_deny/public/_security/denied.php). They load the same helper by a different path depth, which is why a grep scoped to tests/php/ missed them.

Deliberate bare includes are untouched — in particular tests/php/breaker/test_breaker_shutdown_dtor_fatal.php, whose bare require of breaker_redeclare.php exists to trigger exactly this fatal.

Verification

Both sides observed on the hooks profile (PHP_WORKERS: "1" plus WORKER_FILE, so consecutive requests provably land on one worker). Test files are volume-mounted, so neither side needed an image build.

Before, three requests to one swept test:

#1  200  {"test":"gateway_interface","pass":true,...}
#2  500  Fatal error: Cannot redeclare class TestCase ... test_helper.php:5
#3  500  Fatal error: Cannot redeclare class TestCase ... test_helper.php:5

After, five requests, the last two to a different swept file on the same worker — so this also covers the converse, that a class declared by the first test stays visible to a later one rather than merely not being re-declared:

#1..#5  200  valid JSON, all passing

Full suite before and after, 30 profiles: 635 passed / 0 failed / 1 error (636) both times, with byte-identical counts in every profile. The single error is shared/test_channel_async_usevar_stress, a known flake unrelated to this change. The five profiles that consume the fixture files were re-run separately after those three were swept and match their baselines (7 / 3 / 1 / 1 / 1, all passing).

Host checks: cargo fmt -- --check clean, cargo clippy --no-default-features -- -D warnings exit 0, cargo test --no-default-features 955+ passed / 0 failed. No Rust source is touched.

Follow-ups filed separately

  • TestCase::__construct installs set_error_handler and set_exception_handler on every request and removes neither. Under a persistent worker both stacks grow one entry per request — measured at 2, 50 and 500 requests, giving depth 2, 50 and 500, roughly 1.7 KB per request held for the worker's lifetime. register_shutdown_function does not accumulate: five registering requests produced five shutdown runs, not fifteen. This predates the sweep and is untouched by it.
  • Six tests under tests/php/worker/ justify their echo style with a comment saying the helper is pulled in by a bare require. That stopped being true earlier and is now false everywhere, so it reads as a blocker that no longer exists.

Tests:
  - The shared helper declares class TestCase at file level, and a persistent worker keeps its class table between requests, so a test that pulls it in with a bare `require` fatals on the second request one worker serves — reported as HTTP 500 with a non-JSON body naming the wrong test. Nothing running in a worker profile still did this, so the sweep closes the trap ahead of the next test moved into one rather than fixing a live failure.
  - Three fixtures outside tests/php load the same helper by a different path depth, which is why an earlier count missed them.

234 test files changed (no new tests).
@diolektor
diolektor merged commit af6ddff into main Aug 16, 2026
7 checks passed
@diolektor
diolektor deleted the chore/tests-require-once-sweep-test-helper branch August 16, 2026 15:57
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