mirror of
https://github.com/Vateron-Media/XC_VM.git
synced 2026-09-25 12:01:55 +02:00
Move Core/Events into XcVm\Core\Events and its sub-trees: \Contract (StoppableEventInterface), \Auth, \Module, \Settings, \Stream (the event classes), plus root EventDispatcher, ListenerProvider, ListensTo, AbstractEvent. - Namespace 14 files across 6 namespaces; cross-namespace refs imported via use (AbstractEvent/EventDispatcher → Contract\StoppableEventInterface; Stream events → root AbstractEvent). Built-in \Attribute (ListensTo) and ioncube \XC_VM (PackageInstalledEvent) qualified. - Rewrite 'use ListensTo;' → FQCN; add use to referrers across src/ and tests/; fix leading-backslash refs (\EventDispatcher, \ListensTo, \PackageInstalledEvent from the Module commit) to their FQCNs. - Tests: add real top-level use imports to ModuleLoaderBootTest (EventDispatcher) and ListensToAttributeTest (ListensTo) — the use-inserter skipped/mis-placed them due to a namespace() method and a heredoc fixture already containing the FQCN. - phpstan-baseline.neon regenerated (292→292). Completes Core/Container + Core/Events. Verified: php -l clean; PHPStan no errors; PHPUnit 295/295; EventDispatcher resolves and AbstractEvent implements XcVm\Core\Events\Contract\StoppableEventInterface.
139 lines
4.9 KiB
PHP
139 lines
4.9 KiB
PHP
<?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 {}
|