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

Commit 747b7b1

Browse files
committed
Merge branch 'feature/get-listeners' into develop
Close #20
2 parents eddd25d + 15bbca9 commit 747b7b1

File tree

4 files changed

+379
-1
lines changed

4 files changed

+379
-1
lines changed

CHANGELOG.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ All notable changes to this project will be documented in this file, in reverse
5050
- `LazyListenerAggregate`, which, provided a list of `LazyEventListeners` and/or
5151
definitions to use to create them, acts as an aggregate for attaching a number
5252
of such listeners at once.
53+
- [#20](https://github.com/zendframework/zend-eventmanager/pull/20) updates the
54+
trait `Zend\EventManager\Test\EventListenerIntrospectionTrait` so that the
55+
implementation will work with the v3 changes; the tests written for v2
56+
continue to pass, allowing this trait to be used to provide compatibility
57+
testing between v2 and v3.
5358

5459
### Deprecated
5560

@@ -82,6 +87,34 @@ All notable changes to this project will be documented in this file, in reverse
8287

8388
- `FilterIterator::insert()` has been modified to raise an exception if the value provided is not a callable.
8489

90+
## 2.6.2 - 2016-01-12
91+
92+
### Added
93+
94+
- [#19](https://github.com/zendframework/zend-eventmanager/pull/19) adds a new
95+
trait, `Zend\EventManager\Test\EventListenerIntrospectionTrait`, intended for
96+
composition in unit tests. It provides a number of methods that can be used
97+
to retrieve listeners with or without associated priority, and the assertion
98+
`assertListenerAtPriority(callable $listener, $priority, $event, EventManager $events, $message = '')`,
99+
which can be used for testing that a listener was registered at the specified
100+
priority with the specified event.
101+
102+
The features in this patch are intended to facilitate testing against both
103+
version 2 and version 3 of zend-eventmanager, as it provides a consistent API
104+
for retrieving lists of events and listeners between the two versions.
105+
106+
### Deprecated
107+
108+
- Nothing.
109+
110+
### Removed
111+
112+
- Nothing.
113+
114+
### Fixed
115+
116+
- Nothing.
117+
85118
## 2.6.0 - 2015-09-29
86119

87120
### Added

mkdocs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@ pages:
1515
site_name: zend-eventmanager
1616
site_description: 'zend-eventmanager: Powerful event bus system for Zend Framework'
1717
repo_url: 'https://github.com/zendframework/zend-eventmanager'
18-
copyright: 'Copyright (c) 2015 <a href="http://www.zend.com/">Zend Technologies USA Inc.</a>'
18+
copyright: 'Copyright (c) 2016 <a href="http://www.zend.com/">Zend Technologies USA Inc.</a>'
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
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 ReflectionProperty;
14+
use Zend\EventManager\EventManager;
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.
27+
*/
28+
trait EventListenerIntrospectionTrait
29+
{
30+
/**
31+
* Retrieve a list of event names from an event manager.
32+
*
33+
* @param EventManager $events
34+
* @return string[]
35+
*/
36+
private function getEventsFromEventManager(EventManager $events)
37+
{
38+
$r = new ReflectionProperty($events, 'events');
39+
$r->setAccessible(true);
40+
$listeners = $r->getValue($events);
41+
return array_keys($listeners);
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+
$r = new ReflectionProperty($events, 'events');
65+
$r->setAccessible(true);
66+
$listeners = $r->getValue($events);
67+
68+
if (! isset($listeners[$event])) {
69+
return $this->traverseListeners([]);
70+
}
71+
72+
return $this->traverseListeners($listeners[$event], $withPriority);
73+
}
74+
75+
/**
76+
* Assert that a given listener exists at the specified priority.
77+
*
78+
* @param callable $expectedListener
79+
* @param int $expectedPriority
80+
* @param string $event
81+
* @param EventManager $events
82+
* @param string $message Failure message to use, if any.
83+
*/
84+
private function assertListenerAtPriority(
85+
callable $expectedListener,
86+
$expectedPriority,
87+
$event,
88+
EventManager $events,
89+
$message = ''
90+
) {
91+
$message = $message ?: sprintf(
92+
'Listener not found for event "%s" and priority %d',
93+
$event,
94+
$expectedPriority
95+
);
96+
$listeners = $this->getListenersForEvent($event, $events, true);
97+
$found = false;
98+
foreach ($listeners as $priority => $listener) {
99+
if ($listener === $expectedListener
100+
&& $priority === $expectedPriority
101+
) {
102+
$found = true;
103+
break;
104+
}
105+
}
106+
Assert::assertTrue($found, $message);
107+
}
108+
109+
/**
110+
* Returns an indexed array of listeners for an event.
111+
*
112+
* Returns an indexed array of listeners for an event, in priority order.
113+
* Priority values will not be included; use this only for testing if
114+
* specific listeners are present, or for a count of listeners.
115+
*
116+
* @param string $event
117+
* @param EventManager $events
118+
* @return callable[]
119+
*/
120+
private function getArrayOfListenersForEvent($event, EventManager $events)
121+
{
122+
return iterator_to_array($this->getListenersForEvent($event, $events));
123+
}
124+
125+
/**
126+
* Generator for traversing listeners in priority order.
127+
*
128+
* @param array $listeners
129+
* @param bool $withPriority When true, yields priority as key.
130+
*/
131+
public function traverseListeners(array $queue, $withPriority = false)
132+
{
133+
krsort($queue, SORT_NUMERIC);
134+
135+
foreach ($queue as $priority => $listeners) {
136+
$priority = (int) $priority;
137+
foreach ($listeners as $listener) {
138+
if ($withPriority) {
139+
yield $priority => $listener;
140+
} else {
141+
yield $listener;
142+
}
143+
}
144+
}
145+
}
146+
}
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)