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

Commit b4354f7

Browse files
committed
Merge branch 'feature/listener-introspection'
Close #19
2 parents a03de81 + 504954a commit b4354f7

File tree

3 files changed

+363
-0
lines changed

3 files changed

+363
-0
lines changed

CHANGELOG.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,34 @@
22

33
All notable changes to this project will be documented in this file, in reverse chronological order by release.
44

5+
## 2.6.2 - 2016-01-12
6+
7+
### Added
8+
9+
- [#19](https://github.com/zendframework/zend-eventmanager/pull/19) adds a new
10+
trait, `Zend\EventManager\Test\EventListenerIntrospectionTrait`, intended for
11+
composition in unit tests. It provides a number of methods that can be used
12+
to retrieve listeners with or without associated priority, and the assertion
13+
`assertListenerAtPriority(callable $listener, $priority, $event, EventManager $events, $message = '')`,
14+
which can be used for testing that a listener was registered at the specified
15+
priority with the specified event.
16+
17+
The features in this patch are intended to facilitate testing against both
18+
version 2 and version 3 of zend-eventmanager, as it provides a consistent API
19+
for retrieving lists of events and listeners between the two versions.
20+
21+
### Deprecated
22+
23+
- Nothing.
24+
25+
### Removed
26+
27+
- Nothing.
28+
29+
### Fixed
30+
31+
- Nothing.
32+
533
## 2.6.0 - 2015-09-29
634

735
### Added
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
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-2016 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_Assert as Assert;
13+
use Zend\EventManager\EventManager;
14+
use Zend\Stdlib\PriorityQueue;
15+
16+
/**
17+
* Trait providing utility methods and assertions for use in PHPUnit test cases.
18+
*
19+
* This trait may be composed into a test case, and provides:
20+
*
21+
* - methods for introspecting events and listeners
22+
* - methods for asserting listeners are attached at a specific priority
23+
*
24+
* Some functionality in this trait duplicates functionality present in the
25+
* version 2 EventManagerInterface and/or EventManager implementation, but
26+
* abstracts that functionality for use in v3. As such, components or code
27+
* that is testing for listener registration should use the methods in this
28+
* trait to ensure tests are forwards-compatible between zend-eventmanager
29+
* versions.
30+
*/
31+
trait EventListenerIntrospectionTrait
32+
{
33+
/**
34+
* Retrieve a list of event names from an event manager.
35+
*
36+
* @param EventManager $events
37+
* @return string[]
38+
*/
39+
private function getEventsFromEventManager(EventManager $events)
40+
{
41+
return $events->getEvents();
42+
}
43+
44+
/**
45+
* Retrieve an interable list of listeners for an event.
46+
*
47+
* Given an event and an event manager, returns an iterator with the
48+
* listeners for that event, in priority order.
49+
*
50+
* If $withPriority is true, the key values will be the priority at which
51+
* the given listener is attached.
52+
*
53+
* Do not pass $withPriority if you want to cast the iterator to an array,
54+
* as many listeners will likely have the same priority, and thus casting
55+
* will collapse to the last added.
56+
*
57+
* @param string $event
58+
* @param EventManager $events
59+
* @param bool $withPriority
60+
* @return \Traversable
61+
*/
62+
private function getListenersForEvent($event, EventManager $events, $withPriority = false)
63+
{
64+
$listeners = $events->getListeners($event);
65+
return $this->traverseListeners($listeners, $withPriority);
66+
}
67+
68+
/**
69+
* Assert that a given listener exists at the specified priority.
70+
*
71+
* @param callable $expectedListener
72+
* @param int $expectedPriority
73+
* @param string $event
74+
* @param EventManager $events
75+
* @param string $message Failure message to use, if any.
76+
*/
77+
private function assertListenerAtPriority(
78+
callable $expectedListener,
79+
$expectedPriority,
80+
$event,
81+
EventManager $events,
82+
$message = ''
83+
) {
84+
$message = $message ?: sprintf(
85+
'Listener not found for event "%s" and priority %d',
86+
$event,
87+
$expectedPriority
88+
);
89+
$listeners = $this->getListenersForEvent($event, $events, true);
90+
$found = false;
91+
foreach ($listeners as $priority => $listener) {
92+
if ($listener === $expectedListener
93+
&& $priority === $expectedPriority
94+
) {
95+
$found = true;
96+
break;
97+
}
98+
}
99+
Assert::assertTrue($found, $message);
100+
}
101+
102+
/**
103+
* Returns an indexed array of listeners for an event.
104+
*
105+
* Returns an indexed array of listeners for an event, in priority order.
106+
* Priority values will not be included; use this only for testing if
107+
* specific listeners are present, or for a count of listeners.
108+
*
109+
* @param string $event
110+
* @param EventManager $events
111+
* @return callable[]
112+
*/
113+
private function getArrayOfListenersForEvent($event, EventManager $events)
114+
{
115+
return iterator_to_array($this->getListenersForEvent($event, $events));
116+
}
117+
118+
/**
119+
* Generator for traversing listeners in priority order.
120+
*
121+
* @param PriorityQueue $listeners
122+
* @param bool $withPriority When true, yields priority as key.
123+
*/
124+
public function traverseListeners(PriorityQueue $queue, $withPriority = false)
125+
{
126+
foreach ($queue as $handler) {
127+
$listener = $handler->getCallback();
128+
if ($withPriority) {
129+
$priority = (int) $handler->getMetadatum('priority');
130+
yield $priority => $listener;
131+
} else {
132+
yield $listener;
133+
}
134+
}
135+
}
136+
}
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-2016 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)