Passed
Pull Request — master (#14)
by Alex
03:52
created

EventDispatcherTest   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 235
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 14
eloc 77
c 0
b 0
f 0
dl 0
loc 235
rs 10

10 Methods

Rating   Name   Duplication   Size   Complexity  
A getDispatchWillPreventEventPropagationIfItIsStoppedWithinAListenerData() 0 8 1
A getDispatchWillInvokeEventListenersForProvidedEventData() 0 13 1
A setUp() 0 3 1
A testDispatchWillInvokeEventListenersForProvidedEvent() 0 20 2
A testImplementsEventDispatcherInterface() 0 5 1
A testAddListenerForEventWillProxyToInternalListenerProvider() 0 15 1
A testAddListenersForEventWillProxyToInternalListenerProvider() 0 20 1
A testImplementsAddListenerAwareInterface() 0 5 1
A testDispatchWillPreventEventPropagationIfProvidedEventHasPropagationStopped() 0 15 1
A testDispatchWillNotPropagationEventIfItIsStoppedWithinAListener() 0 38 4
1
<?php
2
3
declare(strict_types=1);
4
5
namespace ArpTest\EventDispatcher;
6
7
use Arp\EventDispatcher\EventDispatcher;
8
use Arp\EventDispatcher\Listener\AddableListenerProviderInterface;
9
use Arp\EventDispatcher\Listener\AddListenerAwareInterface;
10
use Arp\EventDispatcher\Listener\Exception\EventListenerException;
11
use Arp\EventDispatcher\Listener\ListenerProvider;
12
use PHPUnit\Framework\MockObject\MockObject;
13
use PHPUnit\Framework\TestCase;
14
use Psr\EventDispatcher\EventDispatcherInterface;
15
use Psr\EventDispatcher\StoppableEventInterface;
16
17
/**
18
 * @author  Alex Patterson <[email protected]>
19
 * @package ArpTest\EventDispatcher
20
 */
21
final class EventDispatcherTest extends TestCase
22
{
23
    /**
24
     * @var AddableListenerProviderInterface|MockObject
25
     */
26
    private $listenerProvider;
27
28
    /**
29
     * Prepare the test dependencies.
30
     *
31
     * @return void
32
     */
33
    public function setUp(): void
34
    {
35
        $this->listenerProvider = $this->getMockForAbstractClass(AddableListenerProviderInterface::class);
36
    }
37
38
    /**
39
     * Ensure that the event manager implements EventDispatcherInterface.
40
     *
41
     * @covers \Arp\EventDispatcher\EventDispatcher
42
     * @covers \Arp\EventDispatcher\AbstractEventDispatcher
43
     */
44
    public function testImplementsEventDispatcherInterface(): void
45
    {
46
        $eventManager = new EventDispatcher($this->listenerProvider);
47
48
        $this->assertInstanceOf(EventDispatcherInterface::class, $eventManager);
49
    }
50
51
    /**
52
     * Ensure that the event manager implements AddListenerAwareInterface.
53
     *
54
     * @covers \Arp\EventDispatcher\EventDispatcher
55
     */
56
    public function testImplementsAddListenerAwareInterface(): void
57
    {
58
        $eventManager = new EventDispatcher($this->listenerProvider);
59
60
        $this->assertInstanceOf(AddListenerAwareInterface::class, $eventManager);
61
    }
62
63
    /**
64
     * If we call dispatch with a StoppableEventInterface that already has propagation stopped, no event listeners
65
     * should be triggered.
66
     *
67
     * @covers \Arp\EventDispatcher\EventDispatcher::dispatch
68
     * @covers \Arp\EventDispatcher\EventDispatcher::isPropagationStopped
69
     */
70
    public function testDispatchWillPreventEventPropagationIfProvidedEventHasPropagationStopped(): void
71
    {
72
        $eventDispatcher = new EventDispatcher($this->listenerProvider);
73
74
        /** @var StoppableEventInterface|MockObject $event */
75
        $event = $this->getMockForAbstractClass(StoppableEventInterface::class);
76
77
        $event->expects($this->once())
78
            ->method('isPropagationStopped')
79
            ->willReturn(true);
80
81
        $this->listenerProvider->expects($this->never())
82
            ->method('getListenersForEvent');
83
84
        $this->assertSame($event, $eventDispatcher->dispatch($event));
85
    }
86
87
    /**
88
     * Assert that the event propagation is stopped if we modify the StoppableEventInterface within an event.
89
     *
90
     * @param integer $listenerCount The number of event listeners attached to the dispatched event.
91
     * @param integer $stopIndex     The index that the event listener should stop propagation.
92
     *
93
     * @dataProvider getDispatchWillPreventEventPropagationIfItIsStoppedWithinAListenerData
94
     *
95
     * @covers \Arp\EventDispatcher\EventDispatcher::dispatch
96
     * @covers \Arp\EventDispatcher\EventDispatcher::isPropagationStopped
97
     */
98
    public function testDispatchWillNotPropagationEventIfItIsStoppedWithinAListener(
99
        int $listenerCount,
100
        int $stopIndex
101
    ): void {
102
        if ($stopIndex >= $listenerCount) {
103
            $this->fail(sprintf(
104
                'The stop index \'%d\' must be less than the number of event listeners \'%d\'.',
105
                $listenerCount,
106
                $stopIndex
107
            ));
108
        }
109
110
        $eventDispatcher = new EventDispatcher($this->listenerProvider);
111
112
        /** @var StoppableEventInterface|MockObject $event */
113
        $event = $this->getMockForAbstractClass(StoppableEventInterface::class);
114
115
        $eventListeners = [];
116
        $isStopped = [];
117
118
        for ($x = 0; $x < $listenerCount; $x++) {
119
            $eventListeners[] = static function (StoppableEventInterface $event) {
0 ignored issues
show
Unused Code introduced by
The parameter $event is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

119
            $eventListeners[] = static function (/** @scrutinizer ignore-unused */ StoppableEventInterface $event) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
120
            };
121
122
            if ($x < ($stopIndex + 1)) {
123
                $isStopped[] = ($x === $stopIndex);
124
            }
125
        }
126
127
        $this->listenerProvider->expects($this->once())
128
            ->method('getListenersForEvent')
129
            ->willReturn($eventListeners);
130
131
        $event->expects($this->exactly(1 + count($isStopped)))
132
            ->method('isPropagationStopped')
133
            ->willReturn(false, ...$isStopped);
134
135
        $this->assertSame($event, $eventDispatcher->dispatch($event));
136
    }
137
138
    /**
139
     * @return array
140
     */
141
    public function getDispatchWillPreventEventPropagationIfItIsStoppedWithinAListenerData(): array
142
    {
143
        return [
144
            [1, 0],
145
            [7, 2],
146
            [23, 20],
147
            [10, 4],
148
            [100, 40],
149
        ];
150
    }
151
152
    /**
153
     * Assert that dispatch() will invoke the require event listeners returned from the listener provider.
154
     *
155
     * @param object $event
156
     * @param int    $numberOfListeners
157
     *
158
     * @dataProvider getDispatchWillInvokeEventListenersForProvidedEventData
159
     *
160
     * @covers \Arp\EventDispatcher\EventDispatcher::dispatch
161
     * @covers \Arp\EventDispatcher\EventDispatcher::isPropagationStopped
162
     */
163
    public function testDispatchWillInvokeEventListenersForProvidedEvent($event, $numberOfListeners = 0): void
164
    {
165
        $eventDispatcher = new EventDispatcher($this->listenerProvider);
166
167
        $listeners = [];
168
169
        for ($x = 0; $x < $numberOfListeners; $x++) {
170
            $listeners[] = static function ($event) {
171
                get_class($event);
172
            };
173
        }
174
175
        $this->listenerProvider->expects($this->once())
176
            ->method('getListenersForEvent')
177
            ->willReturn($listeners);
178
179
        $result = $eventDispatcher->dispatch($event);
180
181
        $this->assertIsObject($result);
182
        $this->assertSame($result, $event);
183
    }
184
185
    /**
186
     * @return array
187
     */
188
    public function getDispatchWillInvokeEventListenersForProvidedEventData(): array
189
    {
190
        return [
191
            [
192
                new \stdClass(),
193
                7,
194
            ],
195
            [
196
                $this->getMockForAbstractClass(StoppableEventInterface::class)
197
                    ->expects($this->exactly(5))
198
                    ->method('isPropagationStopped')
199
                    ->willReturn(false),
200
                5,
201
            ],
202
        ];
203
    }
204
205
    /**
206
     * Assert that calls to addListenerForEvent() proxies to the internal ListenerProvider.
207
     *
208
     * @covers \Arp\EventDispatcher\EventDispatcher::addListenerForEvent
209
     *
210
     * @throws EventListenerException
211
     */
212
    public function testAddListenerForEventWillProxyToInternalListenerProvider(): void
213
    {
214
        $dispatcher = new EventDispatcher($this->listenerProvider);
215
216
        $event = new \stdClass();
217
        $priority = 10;
218
        $listener = static function (\stdClass $event): void {
219
            echo $event->name;
220
        };
221
222
        $this->listenerProvider->expects($this->once())
223
            ->method('addListenerForEvent')
224
            ->with($event, $listener, $priority);
225
226
        $dispatcher->addListenerForEvent($event, $listener, $priority);
227
    }
228
229
    /**
230
     * Assert that calls to addListenerForEvent() proxies to the internal ListenerProvider.
231
     *
232
     * @covers \Arp\EventDispatcher\EventDispatcher::addListenersForEvent
233
     *
234
     * @throws EventListenerException
235
     */
236
    public function testAddListenersForEventWillProxyToInternalListenerProvider(): void
237
    {
238
        $dispatcher = new EventDispatcher($this->listenerProvider);
239
240
        $event = new \stdClass();
241
        $priority = 100;
242
        $listeners = [
243
            static function (\stdClass $event): void {
244
                echo $event->name;
245
            },
246
            static function (\stdClass $event): void {
247
                echo $event->name;
248
            },
249
        ];
250
251
        $this->listenerProvider->expects($this->once())
252
            ->method('addListenersForEvent')
253
            ->with($event, $listeners, $priority);
254
255
        $dispatcher->addListenersForEvent($event, $listeners, $priority);
256
    }
257
}
258