Revolt
Revolt — стандартный event loop для асинхронного PHP: цикл, который ждёт готовности ввода-вывода (таймеров, сокетов, потоков) и по очереди возобновляет корутины, которые этого ждали. На нём работают библиотеки amphp. Плагин предоставляет атрибут #[RunInRevolt]#[RunInRevolt()]Запускает тест на event loop Revolt.: тест выполняется на общем для процесса цикле Revolt и может дожидаться асинхронной работы (таймеры, потоки, Future::await()), не блокируя процесс.
Плагин не требует настройки.
Установка
composer require --dev testo/bridge-revolt#[RunInRevolt]
Запускает тест на event loop Revolt.
#[RunInRevolt()]Тело теста уходит на event loop, а снаружи вызов остаётся обычным блокирующим: пайплайн ждёт, пока тест доработает.
Работает на методе и на классе (Test Case). На классе атрибут прогоняет через цикл каждый тест, но по одному за раз: следующий тест попадает на цикл только после того, как предыдущий полностью завершился.
Примеры:
Дождаться реальной асинхронной работы (таймер Revolt) внутри теста:
use Revolt\EventLoop;
use Testo\Assert;
use Testo\Bridge\Revolt\RunInRevolt;
use Testo\Test;
#[Test]
#[RunInRevolt]
public function resolvesAfterDelay(): void
{
$suspension = EventLoop::getSuspension();
EventLoop::delay(0.1, static fn() => $suspension->resume('ready'));
Assert::same('ready', $suspension->suspend()); // цикл крутится, пока мы ждём
}Приостановка — только через Suspension\Revolt\EventLoop\Suspension
Файбером теста владеет event loop Revolt, поэтому приостанавливаться нужно через Suspension\Revolt\EventLoop\Suspension, привязанную к событию цикла (таймеру, вводу-выводу): голый \Fiber::suspend() на цикле возобновить некому. А если нужны голые файберы под управлением самого Testo, это уже #[RunInFiber]#[RunInFiber(Schedule $schedule = Schedule::Solo)]Запускает тест (метод) или все тесты кейса (класс) в файберах под кооперативным планировщиком Testo. из плагина Fiber, другая задача.
Валидно и обратное: Future::await() и Suspension\Revolt\EventLoop\Suspension работают только в event loop Revolt (под атрибутом #[RunInRevolt]#[RunInRevolt()]Запускает тест на event loop Revolt.).
Один тест за раз
#[RunInRevolt]#[RunInRevolt()]Запускает тест на event loop Revolt. не выполняет тесты параллельно: на event loop уходит только тело теста, и тесты никогда не делят прогон цикла. Провайдер данных, ретраи и служебная обвязка Testo выполняются снаружи, а следующий тест кейса входит на цикл лишь после того, как полностью завершился предыдущий.
Это осознанный размен: изоляция тестов важнее чередования. Если нужно именно чередовать тесты между собой в поисках гонок, возьмите #[RunInFiber]#[RunInFiber(Schedule $schedule = Schedule::Solo)]Запускает тест (метод) или все тесты кейса (класс) в файберах под кооперативным планировщиком Testo. из плагина Fiber: он работает на обычных файберах, которыми управляет сам Testo.
Почему нельзя запустить все тесты одновременно, как с RunInFiber?
Дело в том, кто управляет файберами. Изоляция под #[RunInFiber]#[RunInFiber(Schedule $schedule = Schedule::Solo)]Запускает тест (метод) или все тесты кейса (класс) в файберах под кооперативным планировщиком Testo. модуля Fiber держится на гардах: тело теста выполняется во вложенном файбере гарда, поэтому каждый \Fiber::suspend() проходит сквозь гард, и тот на приостановке снимает состояние своего теста, а на возобновлении подставляет обратно. Под #[RunInRevolt]#[RunInRevolt()]Запускает тест на event loop Revolt. файберы переходят под контроль Revolt: он приостанавливает и возобновляет их сам, мимо гардов, и переключать состояние некому. Окажись на цикле несколько тестов разом, контекст тестов утёк бы: проверки, сообщения и покрытие засчитывались бы чужим тестам.
RunInRevolt или RunInFiber?
#[RunInRevolt]#[RunInRevolt()]Запускает тест на event loop Revolt. — про асинхронность: дать тесту event loop, чтобы он ждал реальный ввод-вывод (amphp, таймеры и потоки Revolt), изолированно от других тестов. #[RunInFiber]#[RunInFiber(Schedule $schedule = Schedule::Solo)]Запускает тест (метод) или все тесты кейса (класс) в файберах под кооперативным планировщиком Testo. — про конкурентность: чередовать тесты кейса на обычных файберах, чтобы находить гонки в кооперативном коде. Это разные задачи: #[RunInRevolt] не запускает тесты параллельно, а #[RunInFiber] не крутит event loop.