Completed
Push — master ( a71bd7...d6582d )
by Alex
15s queued 13s
created

EventDispatcherTest   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 216
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 14
eloc 76
c 0
b 0
f 0
dl 0
loc 216
rs 10
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 PHPUnit\Framework\MockObject\MockObject;
12
use PHPUnit\Framework\TestCase;
13
use Psr\EventDispatcher\EventDispatcherInterface;
14
use Psr\EventDispatcher\StoppableEventInterface;
15
16
/**
17
 * @covers \Arp\EventDispatcher\EventDispatcher
18
 * @covers \Arp\EventDispatcher\AbstractEventDispatcher
19
 */
20
final class EventDispatcherTest extends TestCase
21
{
22
    private AddableListenerProviderInterface&MockObject $listenerProvider;
0 ignored issues
show
Bug introduced by
A parse error occurred: Syntax error, unexpected '&', expecting T_VARIABLE on line 22 at column 44
Loading history...
23
24
    public function setUp(): void
25
    {
26
        $this->listenerProvider = $this->getMockForAbstractClass(AddableListenerProviderInterface::class);
27
    }
28
29
    public function testImplementsEventDispatcherInterface(): void
30
    {
31
        $this->assertInstanceOf(EventDispatcherInterface::class, new EventDispatcher($this->listenerProvider));
32
    }
33
34
    public function testImplementsAddListenerAwareInterface(): void
35
    {
36
        $this->assertInstanceOf(AddListenerAwareInterface::class, new EventDispatcher($this->listenerProvider));
37
    }
38
39
    public function testDispatchWillPreventEventPropagationIfProvidedEventHasPropagationStopped(): void
40
    {
41
        $eventDispatcher = new EventDispatcher($this->listenerProvider);
42
43
        /** @var StoppableEventInterface&MockObject $event */
44
        $event = $this->getMockForAbstractClass(StoppableEventInterface::class);
45
46
        $event->expects($this->once())
47
            ->method('isPropagationStopped')
48
            ->willReturn(true);
49
50
        $this->listenerProvider->expects($this->never())
51
            ->method('getListenersForEvent');
52
53
        $this->assertSame($event, $eventDispatcher->dispatch($event));
54
    }
55
56
    /**
57
     * @dataProvider getDispatchWillPreventEventPropagationIfItIsStoppedWithinAListenerData
58
     */
59
    public function testDispatchWillNotPropagationEventIfItIsStoppedWithinAListener(
60
        int $listenerCount,
61
        int $stopIndex
62
    ): void {
63
        if ($stopIndex >= $listenerCount) {
64
            $this->fail(sprintf(
65
                'The stop index \'%d\' must be less than the number of event listeners \'%d\'.',
66
                $listenerCount,
67
                $stopIndex
68
            ));
69
        }
70
71
        $eventDispatcher = new EventDispatcher($this->listenerProvider);
72
73
        /** @var StoppableEventInterface&MockObject $event */
74
        $event = $this->getMockForAbstractClass(StoppableEventInterface::class);
75
76
        $eventListeners = [];
77
        $isStopped = [];
78
79
        for ($x = 0; $x < $listenerCount; $x++) {
80
            $eventListeners[] = static function () {
81
            };
82
83
            if ($x < ($stopIndex + 1)) {
84
                $isStopped[] = ($x === $stopIndex);
85
            }
86
        }
87
88
        $this->listenerProvider->expects($this->once())
89
            ->method('getListenersForEvent')
90
            ->willReturn($eventListeners);
91
92
        $event->expects($this->exactly(1 + count($isStopped)))
93
            ->method('isPropagationStopped')
94
            ->willReturn(false, ...$isStopped);
95
96
        $this->assertSame($event, $eventDispatcher->dispatch($event));
97
    }
98
99
    /**
100
     * @return array<mixed>
101
     */
102
    public function getDispatchWillPreventEventPropagationIfItIsStoppedWithinAListenerData(): array
103
    {
104
        return [
105
            [1, 0],
106
            [7, 2],
107
            [23, 20],
108
            [10, 4],
109
            [100, 40],
110
        ];
111
    }
112
113
    /**
114
     * @dataProvider getDispatchWillInvokeEventListenersForProvidedEventData
115
     */
116
    public function testDispatchWillInvokeEventListenersForProvidedEvent(
117
        object $event,
118
        int $numberOfListeners = 0
119
    ): void {
120
        $eventDispatcher = new EventDispatcher($this->listenerProvider);
121
122
        $listeners = [];
123
124
        for ($x = 0; $x < $numberOfListeners; $x++) {
125
            $listeners[] = static function ($event) {
126
            };
127
        }
128
129
        $this->listenerProvider->expects($this->once())
130
            ->method('getListenersForEvent')
131
            ->willReturn($listeners);
132
133
        $result = $eventDispatcher->dispatch($event);
134
135
        $this->assertIsObject($result);
136
        $this->assertSame($result, $event);
137
    }
138
139
    /**
140
     * @return array<mixed>
141
     */
142
    public function getDispatchWillInvokeEventListenersForProvidedEventData(): array
143
    {
144
        return [
145
            [
146
                new \stdClass(),
147
                7,
148
            ],
149
            [
150
                $this->getMockForAbstractClass(StoppableEventInterface::class)
151
                    ->expects($this->exactly(5))
152
                    ->method('isPropagationStopped')
153
                    ->willReturn(false),
154
                5,
155
            ],
156
        ];
157
    }
158
159
    /**
160
     * @throws EventListenerException
161
     */
162
    public function testAddListenerForEventWillProxyToInternalListenerProvider(): void
163
    {
164
        $dispatcher = new EventDispatcher($this->listenerProvider);
165
166
        $event = new \stdClass();
167
        $priority = 10;
168
        $listener = static function (\stdClass $event): void {
169
            echo $event->name;
170
        };
171
172
        $this->listenerProvider->expects($this->once())
173
            ->method('addListenerForEvent')
174
            ->with($event, $listener, $priority);
175
176
        $dispatcher->addListenerForEvent($event, $listener, $priority);
177
    }
178
179
    /**
180
     * @throws EventListenerException
181
     */
182
    public function testAddListenersForEventWillProxyToInternalListenerProvider(): void
183
    {
184
        $dispatcher = new EventDispatcher($this->listenerProvider);
185
186
        $event = new \stdClass();
187
        $priority = 100;
188
        $listeners = [
189
            static function (\stdClass $event): void {
190
                echo $event->name;
191
            },
192
            static function (\stdClass $event): void {
193
                echo $event->name;
194
            },
195
        ];
196
197
        $this->listenerProvider->expects($this->once())
198
            ->method('addListenersForEvent')
199
            ->with($event, $listeners, $priority);
200
201
        $dispatcher->addListenersForEvent($event, $listeners, $priority);
202
    }
203
}
204