TraceableEventDispatcherTest.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\EventDispatcher\Tests\Debug;
  11. use PHPUnit\Framework\TestCase;
  12. use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher;
  13. use Symfony\Component\EventDispatcher\Event;
  14. use Symfony\Component\EventDispatcher\EventDispatcher;
  15. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  16. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  17. use Symfony\Component\Stopwatch\Stopwatch;
  18. use Symfony\Contracts\EventDispatcher\Event as ContractsEvent;
  19. class TraceableEventDispatcherTest extends TestCase
  20. {
  21. public function testAddRemoveListener()
  22. {
  23. $dispatcher = new EventDispatcher();
  24. $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
  25. $tdispatcher->addListener('foo', $listener = function () {});
  26. $listeners = $dispatcher->getListeners('foo');
  27. $this->assertCount(1, $listeners);
  28. $this->assertSame($listener, $listeners[0]);
  29. $tdispatcher->removeListener('foo', $listener);
  30. $this->assertCount(0, $dispatcher->getListeners('foo'));
  31. }
  32. public function testGetListeners()
  33. {
  34. $dispatcher = new EventDispatcher();
  35. $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
  36. $tdispatcher->addListener('foo', $listener = function () {});
  37. $this->assertSame($dispatcher->getListeners('foo'), $tdispatcher->getListeners('foo'));
  38. }
  39. public function testHasListeners()
  40. {
  41. $dispatcher = new EventDispatcher();
  42. $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
  43. $this->assertFalse($dispatcher->hasListeners('foo'));
  44. $this->assertFalse($tdispatcher->hasListeners('foo'));
  45. $tdispatcher->addListener('foo', $listener = function () {});
  46. $this->assertTrue($dispatcher->hasListeners('foo'));
  47. $this->assertTrue($tdispatcher->hasListeners('foo'));
  48. }
  49. public function testGetListenerPriority()
  50. {
  51. $dispatcher = new EventDispatcher();
  52. $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
  53. $tdispatcher->addListener('foo', function () {}, 123);
  54. $listeners = $dispatcher->getListeners('foo');
  55. $this->assertSame(123, $tdispatcher->getListenerPriority('foo', $listeners[0]));
  56. // Verify that priority is preserved when listener is removed and re-added
  57. // in preProcess() and postProcess().
  58. $tdispatcher->dispatch(new Event(), 'foo');
  59. $listeners = $dispatcher->getListeners('foo');
  60. $this->assertSame(123, $tdispatcher->getListenerPriority('foo', $listeners[0]));
  61. }
  62. public function testGetListenerPriorityWhileDispatching()
  63. {
  64. $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
  65. $priorityWhileDispatching = null;
  66. $listener = function () use ($tdispatcher, &$priorityWhileDispatching, &$listener) {
  67. $priorityWhileDispatching = $tdispatcher->getListenerPriority('bar', $listener);
  68. };
  69. $tdispatcher->addListener('bar', $listener, 5);
  70. $tdispatcher->dispatch(new Event(), 'bar');
  71. $this->assertSame(5, $priorityWhileDispatching);
  72. }
  73. public function testAddRemoveSubscriber()
  74. {
  75. $dispatcher = new EventDispatcher();
  76. $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
  77. $subscriber = new EventSubscriber();
  78. $tdispatcher->addSubscriber($subscriber);
  79. $listeners = $dispatcher->getListeners('foo');
  80. $this->assertCount(1, $listeners);
  81. $this->assertSame([$subscriber, 'call'], $listeners[0]);
  82. $tdispatcher->removeSubscriber($subscriber);
  83. $this->assertCount(0, $dispatcher->getListeners('foo'));
  84. }
  85. public function testGetCalledListeners()
  86. {
  87. $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
  88. $tdispatcher->addListener('foo', function () {}, 5);
  89. $listeners = $tdispatcher->getNotCalledListeners();
  90. $this->assertArrayHasKey('stub', $listeners[0]);
  91. unset($listeners[0]['stub']);
  92. $this->assertEquals([], $tdispatcher->getCalledListeners());
  93. $this->assertEquals([['event' => 'foo', 'pretty' => 'closure', 'priority' => 5]], $listeners);
  94. $tdispatcher->dispatch(new Event(), 'foo');
  95. $listeners = $tdispatcher->getCalledListeners();
  96. $this->assertArrayHasKey('stub', $listeners[0]);
  97. unset($listeners[0]['stub']);
  98. $this->assertEquals([['event' => 'foo', 'pretty' => 'closure', 'priority' => 5]], $listeners);
  99. $this->assertEquals([], $tdispatcher->getNotCalledListeners());
  100. }
  101. public function testClearCalledListeners()
  102. {
  103. $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
  104. $tdispatcher->addListener('foo', function () {}, 5);
  105. $tdispatcher->dispatch(new Event(), 'foo');
  106. $tdispatcher->reset();
  107. $listeners = $tdispatcher->getNotCalledListeners();
  108. $this->assertArrayHasKey('stub', $listeners[0]);
  109. unset($listeners[0]['stub']);
  110. $this->assertEquals([], $tdispatcher->getCalledListeners());
  111. $this->assertEquals([['event' => 'foo', 'pretty' => 'closure', 'priority' => 5]], $listeners);
  112. }
  113. public function testDispatchContractsEvent()
  114. {
  115. $expectedEvent = new ContractsEvent();
  116. $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
  117. $tdispatcher->addListener('foo', function ($event) use ($expectedEvent) {
  118. $this->assertSame($event, $expectedEvent);
  119. }, 5);
  120. $tdispatcher->dispatch($expectedEvent, 'foo');
  121. $listeners = $tdispatcher->getCalledListeners();
  122. $this->assertArrayHasKey('stub', $listeners[0]);
  123. }
  124. public function testDispatchAfterReset()
  125. {
  126. $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
  127. $tdispatcher->addListener('foo', function () {}, 5);
  128. $tdispatcher->reset();
  129. $tdispatcher->dispatch(new Event(), 'foo');
  130. $listeners = $tdispatcher->getCalledListeners();
  131. $this->assertArrayHasKey('stub', $listeners[0]);
  132. }
  133. public function testGetCalledListenersNested()
  134. {
  135. $tdispatcher = null;
  136. $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
  137. $dispatcher->addListener('foo', function (Event $event, $eventName, $dispatcher) use (&$tdispatcher) {
  138. $tdispatcher = $dispatcher;
  139. $dispatcher->dispatch(new Event(), 'bar');
  140. });
  141. $dispatcher->addListener('bar', function (Event $event) {});
  142. $dispatcher->dispatch(new Event(), 'foo');
  143. $this->assertSame($dispatcher, $tdispatcher);
  144. $this->assertCount(2, $dispatcher->getCalledListeners());
  145. }
  146. public function testItReturnsNoOrphanedEventsWhenCreated()
  147. {
  148. $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
  149. $events = $tdispatcher->getOrphanedEvents();
  150. $this->assertEmpty($events);
  151. }
  152. public function testItReturnsOrphanedEventsAfterDispatch()
  153. {
  154. $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
  155. $tdispatcher->dispatch(new Event(), 'foo');
  156. $events = $tdispatcher->getOrphanedEvents();
  157. $this->assertCount(1, $events);
  158. $this->assertEquals(['foo'], $events);
  159. }
  160. public function testItDoesNotReturnHandledEvents()
  161. {
  162. $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
  163. $tdispatcher->addListener('foo', function () {});
  164. $tdispatcher->dispatch(new Event(), 'foo');
  165. $events = $tdispatcher->getOrphanedEvents();
  166. $this->assertEmpty($events);
  167. }
  168. public function testLogger()
  169. {
  170. $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
  171. $dispatcher = new EventDispatcher();
  172. $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger);
  173. $tdispatcher->addListener('foo', $listener1 = function () {});
  174. $tdispatcher->addListener('foo', $listener2 = function () {});
  175. $logger->expects($this->at(0))->method('debug')->with('Notified event "{event}" to listener "{listener}".', ['event' => 'foo', 'listener' => 'closure']);
  176. $logger->expects($this->at(1))->method('debug')->with('Notified event "{event}" to listener "{listener}".', ['event' => 'foo', 'listener' => 'closure']);
  177. $tdispatcher->dispatch(new Event(), 'foo');
  178. }
  179. public function testLoggerWithStoppedEvent()
  180. {
  181. $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
  182. $dispatcher = new EventDispatcher();
  183. $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger);
  184. $tdispatcher->addListener('foo', $listener1 = function (Event $event) { $event->stopPropagation(); });
  185. $tdispatcher->addListener('foo', $listener2 = function () {});
  186. $logger->expects($this->at(0))->method('debug')->with('Notified event "{event}" to listener "{listener}".', ['event' => 'foo', 'listener' => 'closure']);
  187. $logger->expects($this->at(1))->method('debug')->with('Listener "{listener}" stopped propagation of the event "{event}".', ['event' => 'foo', 'listener' => 'closure']);
  188. $logger->expects($this->at(2))->method('debug')->with('Listener "{listener}" was not called for event "{event}".', ['event' => 'foo', 'listener' => 'closure']);
  189. $tdispatcher->dispatch(new Event(), 'foo');
  190. }
  191. public function testDispatchCallListeners()
  192. {
  193. $called = [];
  194. $dispatcher = new EventDispatcher();
  195. $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
  196. $tdispatcher->addListener('foo', function () use (&$called) { $called[] = 'foo1'; }, 10);
  197. $tdispatcher->addListener('foo', function () use (&$called) { $called[] = 'foo2'; }, 20);
  198. $tdispatcher->dispatch(new Event(), 'foo');
  199. $this->assertSame(['foo2', 'foo1'], $called);
  200. }
  201. public function testDispatchNested()
  202. {
  203. $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
  204. $loop = 1;
  205. $dispatchedEvents = 0;
  206. $dispatcher->addListener('foo', $listener1 = function () use ($dispatcher, &$loop) {
  207. ++$loop;
  208. if (2 == $loop) {
  209. $dispatcher->dispatch(new Event(), 'foo');
  210. }
  211. });
  212. $dispatcher->addListener('foo', function () use (&$dispatchedEvents) {
  213. ++$dispatchedEvents;
  214. });
  215. $dispatcher->dispatch(new Event(), 'foo');
  216. $this->assertSame(2, $dispatchedEvents);
  217. }
  218. public function testDispatchReusedEventNested()
  219. {
  220. $nestedCall = false;
  221. $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
  222. $dispatcher->addListener('foo', function (Event $e) use ($dispatcher) {
  223. $dispatcher->dispatch(new Event(), 'bar', $e);
  224. });
  225. $dispatcher->addListener('bar', function (Event $e) use (&$nestedCall) {
  226. $nestedCall = true;
  227. });
  228. $this->assertFalse($nestedCall);
  229. $dispatcher->dispatch(new Event(), 'foo');
  230. $this->assertTrue($nestedCall);
  231. }
  232. public function testListenerCanRemoveItselfWhenExecuted()
  233. {
  234. $eventDispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
  235. $listener1 = function ($event, $eventName, EventDispatcherInterface $dispatcher) use (&$listener1) {
  236. $dispatcher->removeListener('foo', $listener1);
  237. };
  238. $eventDispatcher->addListener('foo', $listener1);
  239. $eventDispatcher->addListener('foo', function () {});
  240. $eventDispatcher->dispatch(new Event(), 'foo');
  241. $this->assertCount(1, $eventDispatcher->getListeners('foo'), 'expected listener1 to be removed');
  242. }
  243. public function testClearOrphanedEvents()
  244. {
  245. $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
  246. $tdispatcher->dispatch(new Event(), 'foo');
  247. $events = $tdispatcher->getOrphanedEvents();
  248. $this->assertCount(1, $events);
  249. $tdispatcher->reset();
  250. $events = $tdispatcher->getOrphanedEvents();
  251. $this->assertCount(0, $events);
  252. }
  253. }
  254. class EventSubscriber implements EventSubscriberInterface
  255. {
  256. public static function getSubscribedEvents()
  257. {
  258. return ['foo' => 'call'];
  259. }
  260. }