mirror of https://github.com/flightphp/core
				
				
				
			
			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.
		
		
		
		
		
			
		
			
				
					
					
						
							349 lines
						
					
					
						
							12 KiB
						
					
					
				
			
		
		
	
	
							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');
 | 
						|
    }
 | 
						|
}
 |