Skip to content
This repository was archived by the owner on Jan 29, 2020. It is now read-only.

Commit 27400e1

Browse files
committed
Created EventListenerIntrospectionTrait
This trait is intended to provide a forwards-compatibility shim for use when testing event listener registration. Methods mimic some of the functionality already in v2 (via `getEvents()` and `getListeners()`), but in such a way as to allow common behavior when testing the two versions. In particular, it provides the assertion method `assertListenerAtPriority()`, allowing developers to test that a listener was registered for the specified event at the specified priority; this is useful when testing attachment by aggregates or of default listeners.
1 parent eddd25d commit 27400e1

File tree

2 files changed

+336
-0
lines changed

2 files changed

+336
-0
lines changed
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
<?php
2+
/**
3+
* Zend Framework (http://framework.zend.com/)
4+
*
5+
* @link http://github.com/zendframework/zend-eventmanager for the canonical source repository
6+
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
7+
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
8+
*/
9+
10+
namespace Zend\EventManager\Test;
11+
12+
use PHPUnit_Framework_TestCase as TestCase;
13+
use ReflectionProperty;
14+
use Zend\EventManager\EventManager;
15+
use Zend\Stdlib\PriorityQueue;
16+
17+
/**
18+
* Trait providing utility methods and assertions for use in PHPUnit test cases.
19+
*
20+
* This trait may be composed into a test case, and provides:
21+
*
22+
* - methods for introspecting events and listeners
23+
* - methods for asserting listeners are attached at a specific priority
24+
*
25+
* Some functionality in this trait duplicates functionality present in the
26+
* version 2 EventManagerInterface and/or EventManager implementation, but
27+
* abstracts that functionality for use in v3. As such, components or code
28+
* that is testing for listener registration should use the methods in this
29+
* trait to ensure tests are forwards-compatible between zend-eventmanager
30+
* versions.
31+
*/
32+
trait EventListenerIntrospectionTrait
33+
{
34+
/**
35+
* Retrieve a list of event names from an event manager.
36+
*
37+
* @param EventManager $events
38+
* @return string[]
39+
*/
40+
private function getEventsFromEventManager(EventManager $events)
41+
{
42+
return $events->getEvents();
43+
}
44+
45+
/**
46+
* Retrieve an interable list of listeners for an event.
47+
*
48+
* Given an event and an event manager, returns an iterator with the
49+
* listeners for that event, in priority order.
50+
*
51+
* If $withPriority is true, the key values will be the priority at which
52+
* the given listener is attached.
53+
*
54+
* Do not pass $withPriority if you want to cast the iterator to an array,
55+
* as many listeners will likely have the same priority, and thus casting
56+
* will collapse to the last added.
57+
*
58+
* @param string $event
59+
* @param EventManager $events
60+
* @param bool $withPriority
61+
* @return \Traversable
62+
*/
63+
private function getListenersForEvent($event, EventManager $events, $withPriority = false)
64+
{
65+
$listeners = $events->getListeners($event);
66+
return $this->traverseListeners($listeners, $withPriority);
67+
}
68+
69+
/**
70+
* Assert that a given listener exists at the specified priority.
71+
*
72+
* @param callable $expectedListener
73+
* @param int $expectedPriority
74+
* @param string $event
75+
* @param EventManager $events
76+
* @param string $message Failure message to use, if any.
77+
*/
78+
private function assertListenerAtPriority(
79+
callable $expectedListener,
80+
$expectedPriority,
81+
$event,
82+
EventManager $events,
83+
$message = ''
84+
) {
85+
$message = $message ?: sprintf(
86+
'Listener not found for event "%s" and priority %d',
87+
$event,
88+
$expectedPriority
89+
);
90+
$listeners = $this->getListenersForEvent($event, $events, true);
91+
$found = false;
92+
foreach ($listeners as $priority => $listener) {
93+
if ($listener === $expectedListener
94+
&& $priority === $expectedPriority
95+
) {
96+
$found = true;
97+
break;
98+
}
99+
}
100+
TestCase::assertTrue($found, $message);
101+
}
102+
103+
/**
104+
* Returns an indexed array of listeners for an event.
105+
*
106+
* Returns an indexed array of listeners for an event, in priority order.
107+
* Priority values will not be included; use this only for testing if
108+
* specific listeners are present, or for a count of listeners.
109+
*
110+
* @param string $event
111+
* @param EventManager $events
112+
* @return callable[]
113+
*/
114+
private function getArrayOfListenersForEvent($event, EventManager $events)
115+
{
116+
return iterator_to_array($this->getListenersForEvent($event, $events));
117+
}
118+
119+
/**
120+
* Generator for traversing listeners in priority order.
121+
*
122+
* @param PriorityQueue $listeners
123+
* @param bool $withPriority When true, yields priority as key.
124+
*/
125+
public function traverseListeners(PriorityQueue $queue, $withPriority = false)
126+
{
127+
foreach ($queue as $handler) {
128+
$listener = $handler->getCallback();
129+
if ($withPriority) {
130+
$priority = (int) $handler->getMetadatum('priority');
131+
yield $priority => $listener;
132+
} else {
133+
yield $listener;
134+
}
135+
}
136+
}
137+
}
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
<?php
2+
/**
3+
* Zend Framework (http://framework.zend.com/)
4+
*
5+
* @link http://github.com/zendframework/zend-eventmanager for the canonical source repository
6+
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
7+
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
8+
*/
9+
10+
namespace ZendTest\EventManager\Test;
11+
12+
use PHPUnit_Framework_ExpectationFailedException as ExpectationFailedException;
13+
use PHPUnit_Framework_TestCase as TestCase;
14+
use Traversable;
15+
use Zend\EventManager\EventManager;
16+
use Zend\EventManager\Test\EventListenerIntrospectionTrait;
17+
18+
class EventListenerIntrospectionTraitTest extends TestCase
19+
{
20+
use EventListenerIntrospectionTrait;
21+
22+
public function setUp()
23+
{
24+
$this->events = new EventManager();
25+
}
26+
27+
public function testGetEventsFromEventManagerReturnsEventList()
28+
{
29+
// @codingStandardsIgnoreStart
30+
$this->events->attach('foo', function ($e) {});
31+
$this->events->attach('bar', function ($e) {});
32+
$this->events->attach('baz', function ($e) {});
33+
// @codingStandardsIgnoreEnd
34+
35+
$this->assertEquals(['foo', 'bar', 'baz'], $this->getEventsFromEventManager($this->events));
36+
}
37+
38+
public function testGetListenersForEventReturnsIteratorOfListenersForEventInPriorityOrder()
39+
{
40+
// @codingStandardsIgnoreStart
41+
$callback1 = function ($e) {};
42+
$callback2 = function ($e) {};
43+
$callback3 = function ($e) {};
44+
$callback4 = function ($e) {};
45+
$callback5 = function ($e) {};
46+
// @codingStandardsIgnoreEnd
47+
48+
$this->events->attach('foo', $callback5, 1);
49+
$this->events->attach('foo', $callback1, 2);
50+
$this->events->attach('foo', $callback4, 3);
51+
$this->events->attach('foo', $callback3, 4);
52+
$this->events->attach('foo', $callback2, 5);
53+
54+
$listeners = $this->getListenersForEvent('foo', $this->events);
55+
$this->assertInstanceOf(Traversable::class, $listeners);
56+
$listeners = iterator_to_array($listeners);
57+
58+
$this->assertEquals([
59+
$callback5,
60+
$callback1,
61+
$callback4,
62+
$callback3,
63+
$callback2,
64+
], $listeners);
65+
}
66+
67+
public function testGetListenersForEventReturnsIteratorOfListenersInAttachmentOrderWhenSamePriority()
68+
{
69+
// @codingStandardsIgnoreStart
70+
$callback1 = function ($e) {};
71+
$callback2 = function ($e) {};
72+
$callback3 = function ($e) {};
73+
$callback4 = function ($e) {};
74+
$callback5 = function ($e) {};
75+
// @codingStandardsIgnoreEnd
76+
77+
$this->events->attach('foo', $callback5);
78+
$this->events->attach('foo', $callback1);
79+
$this->events->attach('foo', $callback4);
80+
$this->events->attach('foo', $callback3);
81+
$this->events->attach('foo', $callback2);
82+
83+
$listeners = $this->getListenersForEvent('foo', $this->events);
84+
$this->assertInstanceOf(Traversable::class, $listeners);
85+
$listeners = iterator_to_array($listeners);
86+
87+
$this->assertEquals([
88+
$callback5,
89+
$callback1,
90+
$callback4,
91+
$callback3,
92+
$callback2,
93+
], $listeners);
94+
}
95+
96+
public function testGetListenersForEventCanReturnPriorityKeysWhenRequested()
97+
{
98+
// @codingStandardsIgnoreStart
99+
$callback1 = function ($e) {};
100+
$callback2 = function ($e) {};
101+
$callback3 = function ($e) {};
102+
$callback4 = function ($e) {};
103+
$callback5 = function ($e) {};
104+
// @codingStandardsIgnoreEnd
105+
106+
$this->events->attach('foo', $callback5, 1);
107+
$this->events->attach('foo', $callback1, 2);
108+
$this->events->attach('foo', $callback4, 3);
109+
$this->events->attach('foo', $callback3, 4);
110+
$this->events->attach('foo', $callback2, 5);
111+
112+
$listeners = $this->getListenersForEvent('foo', $this->events, true);
113+
$this->assertInstanceOf(Traversable::class, $listeners);
114+
$listeners = iterator_to_array($listeners);
115+
116+
$this->assertEquals([
117+
1 => $callback5,
118+
2 => $callback1,
119+
3 => $callback4,
120+
4 => $callback3,
121+
5 => $callback2,
122+
], $listeners);
123+
}
124+
125+
public function testGetArrayOfListenersForEventReturnsArrayOfListenersInPriorityOrder()
126+
{
127+
// @codingStandardsIgnoreStart
128+
$callback1 = function ($e) {};
129+
$callback2 = function ($e) {};
130+
$callback3 = function ($e) {};
131+
$callback4 = function ($e) {};
132+
$callback5 = function ($e) {};
133+
// @codingStandardsIgnoreEnd
134+
135+
$this->events->attach('foo', $callback5, 1);
136+
$this->events->attach('foo', $callback1, 1);
137+
$this->events->attach('foo', $callback4, 3);
138+
$this->events->attach('foo', $callback3, 2);
139+
$this->events->attach('foo', $callback2, 2);
140+
141+
$listeners = $this->getArrayOfListenersForEvent('foo', $this->events);
142+
$this->assertInternalType('array', $listeners);
143+
144+
$this->assertEquals([
145+
$callback5,
146+
$callback1,
147+
$callback3,
148+
$callback2,
149+
$callback4,
150+
], $listeners);
151+
}
152+
153+
public function testAssertListenerAtPriorityPassesWhenListenerIsFound()
154+
{
155+
// @codingStandardsIgnoreStart
156+
$callback = function ($e) {};
157+
// @codingStandardsIgnoreEnd
158+
159+
$this->events->attach('foo', $callback, 7);
160+
161+
$this->assertListenerAtPriority($callback, 7, 'foo', $this->events);
162+
}
163+
164+
public function testAssertListenerAtPriorityFailsWhenListenerIsNotFound()
165+
{
166+
// @codingStandardsIgnoreStart
167+
$event = 'foo';
168+
$listener = function ($e) {};
169+
$priority = 7;
170+
$this->events->attach($event, $listener, $priority);
171+
172+
$alternate = function ($e) {};
173+
174+
$permutations = [
175+
'different-listener' => ['listener' => $alternate, 'priority' => $priority, 'event' => $event],
176+
'different-priority' => ['listener' => $listener, 'priority' => $priority + 1, 'event' => $event],
177+
'different-event' => ['listener' => $listener, 'priority' => $priority, 'event' => $event . '-FOO'],
178+
];
179+
// @codingStandardsIgnoreEnd
180+
181+
foreach ($permutations as $case => $arguments) {
182+
try {
183+
$this->assertListenerAtPriority(
184+
$arguments['listener'],
185+
$arguments['priority'],
186+
$arguments['event'],
187+
$this->events
188+
);
189+
$this->fail('assertListenerAtPriority assertion had a false positive for case ' . $case);
190+
} catch (ExpectationFailedException $e) {
191+
$this->assertContains(sprintf(
192+
'Listener not found for event "%s" and priority %d',
193+
$arguments['event'],
194+
$arguments['priority']
195+
), $e->getMessage(), sprintf('Assertion failure message was unexpected: %s', $e->getMessage()));
196+
}
197+
}
198+
}
199+
}

0 commit comments

Comments
 (0)