Files
XC_VM/tests/Unit/EventDispatcherTest.php
T

139 lines
4.9 KiB
PHP
Raw Normal View History

<?php
use XcVm\Core\Events\Contract\StoppableEventInterface;
use XcVm\Core\Events\EventDispatcher;
use XcVm\Core\Events\AbstractEvent;
use PHPUnit\Framework\TestCase;
final class EventDispatcherTest extends TestCase {
protected function setUp(): void {
EventDispatcher::clear();
}
protected function tearDown(): void {
EventDispatcher::clear();
}
// ── dispatch / listen ──────────────────────────────────────
public function testDispatchCallsMatchingListener(): void {
$received = null;
EventDispatcher::listen(TestPlainEvent::class, function (TestPlainEvent $e) use (&$received) {
$received = $e->value;
});
EventDispatcher::dispatch(new TestPlainEvent('hello'));
$this->assertSame('hello', $received);
}
public function testDispatchReturnsEvent(): void {
$event = new TestPlainEvent('x');
$returned = EventDispatcher::dispatch($event);
$this->assertSame($event, $returned);
}
public function testDispatchDoesNotCallUnrelatedListeners(): void {
$called = false;
EventDispatcher::listen(TestPlainEvent::class, function () use (&$called) {
$called = true;
});
EventDispatcher::dispatch(new TestOtherEvent());
$this->assertFalse($called);
}
// ── Priority ordering ──────────────────────────────────────
public function testListenersAreCalledInPriorityDescendingOrder(): void {
$order = [];
EventDispatcher::listen(TestPlainEvent::class, function () use (&$order) { $order[] = 'low'; }, 10);
EventDispatcher::listen(TestPlainEvent::class, function () use (&$order) { $order[] = 'high'; }, 50);
EventDispatcher::listen(TestPlainEvent::class, function () use (&$order) { $order[] = 'mid'; }, 30);
EventDispatcher::dispatch(new TestPlainEvent('x'));
$this->assertSame(['high', 'mid', 'low'], $order);
}
// ── StoppableEventInterface ────────────────────────────────
public function testStoppableEventStopsPropagation(): void {
$calls = 0;
EventDispatcher::listen(TestStoppableEvent::class, function (TestStoppableEvent $e) use (&$calls) {
$calls++;
$e->stopPropagation();
}, 10);
EventDispatcher::listen(TestStoppableEvent::class, function () use (&$calls) {
$calls++;
}, 5);
EventDispatcher::dispatch(new TestStoppableEvent());
$this->assertSame(1, $calls);
}
// ── hasListeners ───────────────────────────────────────────
public function testHasListenersReturnsTrueWhenRegistered(): void {
EventDispatcher::listen(TestPlainEvent::class, fn() => null);
$this->assertTrue(EventDispatcher::hasListeners(TestPlainEvent::class));
}
public function testHasListenersReturnsFalseWhenNone(): void {
$this->assertFalse(EventDispatcher::hasListeners(TestPlainEvent::class));
}
// ── unlisten / clear ───────────────────────────────────────
public function testUnlistenRemovesAllListenersForEvent(): void {
$called = false;
EventDispatcher::listen(TestPlainEvent::class, fn() => $called = true);
EventDispatcher::unlisten(TestPlainEvent::class);
EventDispatcher::dispatch(new TestPlainEvent('x'));
$this->assertFalse($called);
}
public function testClearRemovesAllListeners(): void {
$called = false;
EventDispatcher::listen(TestPlainEvent::class, fn() => $called = true);
EventDispatcher::clear();
EventDispatcher::dispatch(new TestPlainEvent('x'));
$this->assertFalse($called);
}
// ── Multiple listeners on same event ───────────────────────
public function testMultipleListenersAllCalled(): void {
$results = [];
EventDispatcher::listen(TestPlainEvent::class, function () use (&$results) { $results[] = 'a'; });
EventDispatcher::listen(TestPlainEvent::class, function () use (&$results) { $results[] = 'b'; });
EventDispatcher::dispatch(new TestPlainEvent('x'));
$this->assertCount(2, $results);
}
}
// ── Test event fixtures ────────────────────────────────────────────
final class TestPlainEvent {
public function __construct(public readonly string $value = '') {}
}
final class TestOtherEvent {}
final class TestStoppableEvent extends AbstractEvent {}