You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
flight-core/tests/EventSystemTest.php

349 lines
12 KiB

<?php
declare(strict_types=1);
namespace flight\tests;
use Flight;
use PHPUnit\Framework\TestCase;
use flight\Engine;
use TypeError;
class EventSystemTest extends TestCase
{
protected function setUp(): void
{
// Reset the Flight engine before each test to ensure a clean state
Flight::setEngine(new Engine());
Flight::app()->init();
Flight::eventDispatcher()->resetInstance(); // Clear any existing listeners
}
/**
* Test registering and triggering a single listener.
*/
public function testRegisterAndTriggerSingleListener()
{
$called = false;
Flight::onEvent('test.event', function () use (&$called) {
$called = true;
});
Flight::triggerEvent('test.event');
$this->assertTrue($called, 'Single listener should be called when event is triggered.');
}
/**
* Test registering multiple listeners for the same event.
*/
public function testRegisterMultipleListeners()
{
$counter = 0;
Flight::onEvent('test.event', function () use (&$counter) {
$counter++;
});
Flight::onEvent('test.event', function () use (&$counter) {
$counter++;
});
Flight::triggerEvent('test.event');
$this->assertEquals(2, $counter, 'All registered listeners should be called.');
}
/**
* Test triggering an event with no listeners registered.
*/
public function testTriggerWithNoListeners()
{
// Should not throw any errors
Flight::triggerEvent('non.existent.event');
$this->assertTrue(true, 'Triggering an event with no listeners should not throw an error.');
}
/**
* Test that a listener receives a single argument correctly.
*/
public function testListenerReceivesSingleArgument()
{
$received = null;
Flight::onEvent('test.event', function ($arg) use (&$received) {
$received = $arg;
});
Flight::triggerEvent('test.event', 'hello');
$this->assertEquals('hello', $received, 'Listener should receive the passed argument.');
}
/**
* Test that a listener receives multiple arguments correctly.
*/
public function testListenerReceivesMultipleArguments()
{
$received = [];
Flight::onEvent('test.event', function ($arg1, $arg2) use (&$received) {
$received = [$arg1, $arg2];
});
Flight::triggerEvent('test.event', 'first', 'second');
$this->assertEquals(['first', 'second'], $received, 'Listener should receive all passed arguments.');
}
/**
* Test that listeners are called in the order they were registered.
*/
public function testListenersCalledInOrder()
{
$order = [];
Flight::onEvent('test.event', function () use (&$order) {
$order[] = 1;
});
Flight::onEvent('test.event', function () use (&$order) {
$order[] = 2;
});
Flight::triggerEvent('test.event');
$this->assertEquals([1, 2], $order, 'Listeners should be called in registration order.');
}
/**
* Test that listeners are not called for unrelated events.
*/
public function testListenerNotCalledForOtherEvents()
{
$called = false;
Flight::onEvent('test.event1', function () use (&$called) {
$called = true;
});
Flight::triggerEvent('test.event2');
$this->assertFalse($called, 'Listeners should not be called for different events.');
}
/**
* Test overriding the onEvent method.
*/
public function testOverrideOnEvent()
{
$called = false;
Flight::map('onEvent', function ($event, $callback) use (&$called) {
$called = true;
});
Flight::onEvent('test.event', function () {
});
$this->assertTrue($called, 'Overridden onEvent method should be called.');
}
/**
* Test overriding the triggerEvent method.
*/
public function testOverrideTriggerEvent()
{
$called = false;
Flight::map('triggerEvent', function ($event, ...$args) use (&$called) {
$called = true;
});
Flight::triggerEvent('test.event');
$this->assertTrue($called, 'Overridden triggerEvent method should be called.');
}
/**
* Test that an overridden onEvent can still register listeners by calling the original method.
*/
public function testOverrideOnEventStillRegistersListener()
{
$overrideCalled = false;
Flight::map('onEvent', function ($event, $callback) use (&$overrideCalled) {
$overrideCalled = true;
// Call the original method
Flight::app()->_onEvent($event, $callback);
});
$listenerCalled = false;
Flight::onEvent('test.event', function () use (&$listenerCalled) {
$listenerCalled = true;
});
Flight::triggerEvent('test.event');
$this->assertTrue($overrideCalled, 'Overridden onEvent should be called.');
$this->assertTrue($listenerCalled, 'Listener should still be triggered after override.');
}
/**
* Test that an overridden triggerEvent can still trigger listeners by calling the original method.
*/
public function testOverrideTriggerEventStillTriggersListeners()
{
$overrideCalled = false;
Flight::map('triggerEvent', function ($event, ...$args) use (&$overrideCalled) {
$overrideCalled = true;
// Call the original method
Flight::app()->_triggerEvent($event, ...$args);
});
$listenerCalled = false;
Flight::onEvent('test.event', function () use (&$listenerCalled) {
$listenerCalled = true;
});
Flight::triggerEvent('test.event');
$this->assertTrue($overrideCalled, 'Overridden triggerEvent should be called.');
$this->assertTrue($listenerCalled, 'Listeners should still be triggered after override.');
}
/**
* Test that an invalid callable throws an exception (if applicable).
*/
public function testInvalidCallableThrowsException()
{
$this->expectException(TypeError::class);
// Assuming the event system validates callables
Flight::onEvent('test.event', 'not_a_callable');
}
/**
* Test that event propagation stops if a listener returns false.
*/
public function testStopPropagation()
{
$firstCalled = false;
$secondCalled = false;
$thirdCalled = false;
Flight::onEvent('test.event', function () use (&$firstCalled) {
$firstCalled = true;
return true; // Continue propagation
});
Flight::onEvent('test.event', function () use (&$secondCalled) {
$secondCalled = true;
return false; // Stop propagation
});
Flight::onEvent('test.event', function () use (&$thirdCalled) {
$thirdCalled = true;
});
Flight::triggerEvent('test.event');
$this->assertTrue($firstCalled, 'First listener should be called');
$this->assertTrue($secondCalled, 'Second listener should be called');
$this->assertFalse($thirdCalled, 'Third listener should not be called after propagation stopped');
}
/**
* Test that hasListeners() correctly identifies events with listeners.
*/
public function testHasListeners()
{
$this->assertFalse(Flight::eventDispatcher()->hasListeners('test.event'), 'Event should not have listeners before registration');
Flight::onEvent('test.event', function () {
});
$this->assertTrue(Flight::eventDispatcher()->hasListeners('test.event'), 'Event should have listeners after registration');
}
/**
* Test that getListeners() returns the correct listeners for an event.
*/
public function testGetListeners()
{
$callback1 = function () {
};
$callback2 = function () {
};
$this->assertEmpty(Flight::eventDispatcher()->getListeners('test.event'), 'Event should have no listeners before registration');
Flight::onEvent('test.event', $callback1);
Flight::onEvent('test.event', $callback2);
$listeners = Flight::eventDispatcher()->getListeners('test.event');
$this->assertCount(2, $listeners, 'Event should have two registered listeners');
$this->assertSame($callback1, $listeners[0], 'First listener should match the first callback');
$this->assertSame($callback2, $listeners[1], 'Second listener should match the second callback');
}
/**
* Test that getListeners() returns an empty array for events with no listeners.
*/
public function testGetListenersForNonexistentEvent()
{
$listeners = Flight::eventDispatcher()->getListeners('nonexistent.event');
$this->assertIsArray($listeners, 'Should return an array for nonexistent events');
$this->assertEmpty($listeners, 'Should return an empty array for nonexistent events');
}
/**
* Test that getAllRegisteredEvents() returns all event names with registered listeners.
*/
public function testGetAllRegisteredEvents()
{
$this->assertEmpty(Flight::eventDispatcher()->getAllRegisteredEvents(), 'No events should be registered initially');
Flight::onEvent('test.event1', function () {
});
Flight::onEvent('test.event2', function () {
});
$events = Flight::eventDispatcher()->getAllRegisteredEvents();
$this->assertCount(2, $events, 'Should return all registered event names');
$this->assertContains('test.event1', $events, 'Should contain the first event');
$this->assertContains('test.event2', $events, 'Should contain the second event');
}
/**
* Test that removeListener() correctly removes a specific listener from an event.
*/
public function testRemoveListener()
{
$callback1 = function () {
return 'callback1';
};
$callback2 = function () {
return 'callback2';
};
Flight::onEvent('test.event', $callback1);
Flight::onEvent('test.event', $callback2);
$this->assertCount(2, Flight::eventDispatcher()->getListeners('test.event'), 'Event should have two listeners initially');
Flight::eventDispatcher()->removeListener('test.event', $callback1);
$listeners = Flight::eventDispatcher()->getListeners('test.event');
$this->assertCount(1, $listeners, 'Event should have one listener after removal');
$this->assertSame($callback2, $listeners[0], 'Remaining listener should be the second callback');
}
/**
* Test that removeAllListeners() correctly removes all listeners for an event.
*/
public function testRemoveAllListeners()
{
Flight::onEvent('test.event', function () {
});
Flight::onEvent('test.event', function () {
});
Flight::onEvent('another.event', function () {
});
$this->assertTrue(Flight::eventDispatcher()->hasListeners('test.event'), 'Event should have listeners before removal');
$this->assertTrue(Flight::eventDispatcher()->hasListeners('another.event'), 'Another event should have listeners');
Flight::eventDispatcher()->removeAllListeners('test.event');
$this->assertFalse(Flight::eventDispatcher()->hasListeners('test.event'), 'Event should have no listeners after removal');
$this->assertTrue(Flight::eventDispatcher()->hasListeners('another.event'), 'Another event should still have listeners');
}
/**
* Test that trying to remove listeners for nonexistent events doesn't cause errors.
*/
public function testRemoveListenersForNonexistentEvent()
{
// Should not throw any errors
Flight::eventDispatcher()->removeListener('nonexistent.event', function () {
});
Flight::eventDispatcher()->removeAllListeners('nonexistent.event');
$this->assertTrue(true, 'Removing listeners for nonexistent events should not throw errors');
}
}