testo/bridge-revolt

Revolt event-loop bridge for the Testo testing framework: run tests on the real event loop for async I/O.

Maintainers

Package info

github.com/php-testo/bridge-revolt

pkg:composer/testo/bridge-revolt

Transparency log

Statistics

Installs: 0

Dependents: 1

Suggesters: 0

Stars: 0

0.1.1 2026-08-06 07:43 UTC

This package is auto-updated.

Last update: 2026-08-06 07:44:22 UTC


README

TESTO

Revolt event-loop bridge

Documentation Support on Boosty


Important

🪞 This is a read-only mirror.

Active development of the Testo project lives in php-testo/testo under bridge/revolt/. This repository is automatically synchronized from there on every release.

File issues and pull requests in the main monorepo, not here.

About

Runs a test on the process-global Revolt event loop, so the test body may await real async work (timers, streams, Future::await(), amphp libraries) and resume without blocking the process.

  • #[RunInRevolt] — run a test on the Revolt event loop for real async I/O. Applied to a class, it drives every test in the case.

Suspension must go through a Revolt Suspension bound to a watcher (I/O, timer) — a bare \Fiber::suspend() has no resumer on the loop (that is the plain-fiber #[RunInFiber] from testo/fiber, a different concern).

One test at a time

Only the test body is placed on the loop — right before the test, inner to the data provider, retries and every scoped-state guard — and the next test only enters once it has finished. Tests of a case never share a loop run, and the framework pipeline itself never parks on the loop.

That is what keeps the coroutines a test spawns attributable to it. Testo's scoped-state guards keep one active state and swap it only at fiber switches they drive themselves — and the loop's switches belong to the Revolt driver, which resumes parked fibers directly, past anything wrapping them. So the guards open their scopes outside the loop and never park during the dispatch: for as long as the test runs, the active state is simply this test's, and a coroutine started with EventLoop::queue() or async() reads it like the body does, however deep it nests. A second test on the same loop run would need that state swapped at switches nobody but the driver controls — hence one test at a time.

To interleave whole tests with each other, use #[RunInFiber] from testo/fiber — those run on plain fibers Testo itself drives.

use Testo\Bridge\Revolt\RunInRevolt;

#[RunInRevolt]
final class TimersTest { /* ... */ }

Install

composer require --dev testo/bridge-revolt

PHP Latest Version on Packagist License Total Downloads