90 lines
2.7 KiB
PHP
90 lines
2.7 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
declare(strict_types=1);
|
||
|
|
|
||
|
|
namespace Blax\ReactPhpKernel\Tests;
|
||
|
|
|
||
|
|
use Blax\ReactPhpKernel\Contracts\Server;
|
||
|
|
use Blax\ReactPhpKernel\Kernel;
|
||
|
|
use Blax\ReactPhpKernel\Tests\Fixtures\RecordingServer;
|
||
|
|
use PHPUnit\Framework\TestCase;
|
||
|
|
use React\EventLoop\LoopInterface;
|
||
|
|
use React\EventLoop\StreamSelectLoop;
|
||
|
|
|
||
|
|
final class KernelTest extends TestCase
|
||
|
|
{
|
||
|
|
public function test_uses_the_injected_loop(): void
|
||
|
|
{
|
||
|
|
$loop = new StreamSelectLoop;
|
||
|
|
|
||
|
|
$this->assertSame($loop, (new Kernel($loop))->loop());
|
||
|
|
}
|
||
|
|
|
||
|
|
public function test_boots_and_shuts_down_registered_servers_with_hooks(): void
|
||
|
|
{
|
||
|
|
$loop = new StreamSelectLoop;
|
||
|
|
$kernel = new Kernel($loop);
|
||
|
|
|
||
|
|
$server = new RecordingServer;
|
||
|
|
// Stop the kernel on the next tick so run() returns instead of blocking.
|
||
|
|
$server->onBoot = fn (LoopInterface $l) => $l->futureTick(fn () => $kernel->stop());
|
||
|
|
|
||
|
|
$bootHook = false;
|
||
|
|
$shutdownHook = false;
|
||
|
|
|
||
|
|
$kernel
|
||
|
|
->register($server)
|
||
|
|
->onBoot(function () use (&$bootHook) {
|
||
|
|
$bootHook = true;
|
||
|
|
})
|
||
|
|
->onShutdown(function () use (&$shutdownHook) {
|
||
|
|
$shutdownHook = true;
|
||
|
|
})
|
||
|
|
->run();
|
||
|
|
|
||
|
|
$this->assertTrue($server->booted, 'server should have been booted');
|
||
|
|
$this->assertTrue($server->wasShutDown, 'server should have been shut down');
|
||
|
|
$this->assertTrue($bootHook, 'onBoot hook should have fired');
|
||
|
|
$this->assertTrue($shutdownHook, 'onShutdown hook should have fired');
|
||
|
|
$this->assertFalse($kernel->isRunning());
|
||
|
|
}
|
||
|
|
|
||
|
|
public function test_stop_is_idempotent_and_safe_before_run(): void
|
||
|
|
{
|
||
|
|
$kernel = new Kernel(new StreamSelectLoop);
|
||
|
|
|
||
|
|
$kernel->stop(); // never ran — must be a harmless no-op
|
||
|
|
$kernel->stop();
|
||
|
|
|
||
|
|
$this->assertFalse($kernel->isRunning());
|
||
|
|
}
|
||
|
|
|
||
|
|
public function test_a_failing_server_shutdown_does_not_block_the_others(): void
|
||
|
|
{
|
||
|
|
$loop = new StreamSelectLoop;
|
||
|
|
$kernel = new Kernel($loop);
|
||
|
|
|
||
|
|
$throwing = new class implements Server
|
||
|
|
{
|
||
|
|
public function name(): string
|
||
|
|
{
|
||
|
|
return 'throwing';
|
||
|
|
}
|
||
|
|
|
||
|
|
public function boot(LoopInterface $loop): void {}
|
||
|
|
|
||
|
|
public function shutdown(): void
|
||
|
|
{
|
||
|
|
throw new \RuntimeException('teardown failed');
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
$good = new RecordingServer;
|
||
|
|
$good->onBoot = fn (LoopInterface $l) => $l->futureTick(fn () => $kernel->stop());
|
||
|
|
|
||
|
|
$kernel->register($throwing)->register($good)->run();
|
||
|
|
|
||
|
|
$this->assertTrue($good->wasShutDown, 'a throwing teardown must not stop the others');
|
||
|
|
}
|
||
|
|
}
|