Skip to content

Commit 9b2ba43

Browse files
authored
Merge pull request #10 from w3c/toooni-fix_multi_dispatch-tests
prevent events from being dispatched multiple times - tests
2 parents 738aae9 + e2e8ea9 commit 9b2ba43

File tree

2 files changed

+195
-2
lines changed

2 files changed

+195
-2
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
namespace W3C\LifecycleEventsBundle\Tests\Services\Fixtures;
4+
5+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
6+
use W3C\LifecycleEventsBundle\Annotation\Create;
7+
use W3C\LifecycleEventsBundle\Event\Definitions\LifecycleEvents;
8+
use W3C\LifecycleEventsBundle\Services\LifecycleEventsDispatcher;
9+
10+
class MySubscriber implements EventSubscriberInterface
11+
{
12+
13+
private $ran = false;
14+
15+
private $dispatcher;
16+
private $annotation;
17+
private $args;
18+
19+
public function __construct(LifecycleEventsDispatcher $dispatcher, $annotation, $args)
20+
{
21+
$this->dispatcher = $dispatcher;
22+
$this->annotation = $annotation;
23+
$this->args = $args;
24+
}
25+
26+
/**
27+
* @inheritdoc
28+
*/
29+
public static function getSubscribedEvents()
30+
{
31+
return [
32+
LifecycleEvents::CREATED => 'onCalled',
33+
LifecycleEvents::DELETED => 'onCalled',
34+
LifecycleEvents::UPDATED => 'onCalled',
35+
LifecycleEvents::PROPERTY_CHANGED => 'onCalled',
36+
LifecycleEvents::COLLECTION_CHANGED => 'onCalled',
37+
];
38+
}
39+
40+
public function onCalled()
41+
{
42+
if (!$this->ran) {
43+
$this->ran = true; // we don't want to run in an infinite loop
44+
$this->dispatcher->addCreation(new Create(), $this->args);
45+
$this->dispatcher->dispatchEvents();
46+
}
47+
}
48+
}

Tests/Services/LifecycleEventsDispatcherTest.php

Lines changed: 147 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
66
use Doctrine\Common\Persistence\ObjectManager;
77
use Doctrine\ORM\Mapping\ClassMetadata;
8+
use W3C\LifecycleEventsBundle\Tests\Services\Fixtures\MySubscriber;
89
use PHPUnit\Framework\TestCase;
10+
use Symfony\Component\EventDispatcher\EventDispatcher;
911
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
1012
use W3C\LifecycleEventsBundle\Annotation\Change;
1113
use W3C\LifecycleEventsBundle\Annotation\Create;
@@ -56,8 +58,8 @@ public function setUp()
5658
parent::setUp();
5759

5860
$this->sfDispatcher = $this
59-
->getMockBuilder(EventDispatcherInterface::class)
60-
->disableOriginalConstructor()
61+
->getMockBuilder(EventDispatcher::class)
62+
->enableProxyingToOriginalMethods()
6163
->getMock();
6264
;
6365
$this->dispatcher = new LifecycleEventsDispatcher($this->sfDispatcher, true);
@@ -81,6 +83,7 @@ public function testDispatchCreationEvents()
8183
$this->assertCount(1, $this->dispatcher->getCreations());
8284

8385
$expectedEvent = new LifecycleEvent($user);
86+
8487
$this->sfDispatcher->expects($this->once())
8588
->method('dispatch')
8689
->with(LifecycleEvents::CREATED, $expectedEvent)
@@ -89,6 +92,29 @@ public function testDispatchCreationEvents()
8992
$this->dispatcher->dispatchEvents();
9093
}
9194

95+
/**
96+
* Test that if dispatchEvents is called recursively (could happen if flush happens in a listener),
97+
* events already fired aren't a second time.
98+
*/
99+
public function testDispatchCreationEventsRecursive()
100+
{
101+
$user = new User();
102+
$annotation = new Create();
103+
$args = new LifecycleEventArgs($user, $this->objectManager);
104+
105+
$this->sfDispatcher->addSubscriber(new MySubscriber($this->dispatcher, $annotation, $args));
106+
107+
$this->dispatcher->addCreation($annotation, $args);
108+
109+
$this->assertCount(1, $this->dispatcher->getCreations());
110+
111+
// 2 === 1 addCreation above + 1 in MySubscriber::onCalled
112+
$this->sfDispatcher->expects($this->exactly(2))
113+
->method('dispatch');
114+
115+
$this->dispatcher->dispatchEvents();
116+
}
117+
92118
public function testDispatchCreationEventsCustom()
93119
{
94120
$user = new User();
@@ -135,6 +161,37 @@ public function testDispatchDeletionEvents()
135161
$this->dispatcher->dispatchEvents();
136162
}
137163

164+
/**
165+
* Test that if dispatchEvents is called recursively (could happen if flush happens in a listener),
166+
* events already fired aren't a second time.
167+
*/
168+
public function testDispatchDeletionEventsRecursive()
169+
{
170+
$user = new User();
171+
$annotation = new Delete();
172+
$args = new LifecycleEventArgs($user, $this->objectManager);
173+
174+
$this->sfDispatcher->addSubscriber(new MySubscriber($this->dispatcher, $annotation, $args));
175+
176+
$this->objectManager->method('getClassMetadata')->willReturn($this->classMetadata);
177+
$this->classMetadata->expects($this->once())
178+
->method('getIdentifierFieldNames')
179+
->willReturn(['name']);
180+
$this->classMetadata->expects($this->once())
181+
->method('getIdentifierValues')
182+
->willReturn(['toto']);
183+
184+
$this->dispatcher->addDeletion($annotation, $args);
185+
186+
$this->assertCount(1, $this->dispatcher->getDeletions());
187+
188+
// 2 === 1 addDeletion above + 1 addCreation in MySubscriber::onCalled
189+
$this->sfDispatcher->expects($this->exactly(2))
190+
->method('dispatch');
191+
192+
$this->dispatcher->dispatchEvents();
193+
}
194+
138195
public function testDispatchDeletionEventsCustom()
139196
{
140197
$user = new User();
@@ -185,6 +242,34 @@ public function testDispatchUpdatesEvents()
185242
$this->dispatcher->dispatchEvents();
186243
}
187244

245+
/**
246+
* Test that if dispatchEvents is called recursively (could happen if flush happens in a listener),
247+
* events already fired aren't a second time.
248+
*/
249+
public function testDispatchUpdatesEventsRecursive()
250+
{
251+
$user = new User();
252+
$annotation = new Update();
253+
$args = new LifecycleEventArgs($user, $this->objectManager);
254+
255+
$this->sfDispatcher->addSubscriber(new MySubscriber($this->dispatcher, $annotation, $args));
256+
257+
$this->dispatcher->addUpdate(
258+
$annotation,
259+
$user,
260+
['name' => ['foo', 'bar']],
261+
[]
262+
);
263+
264+
$this->assertCount(1, $this->dispatcher->getUpdates());
265+
266+
// 2 === 1 addUpdate above + 1 addCreation in MySubscriber::onCalled
267+
$this->sfDispatcher->expects($this->exactly(2))
268+
->method('dispatch');
269+
270+
$this->dispatcher->dispatchEvents();
271+
}
272+
188273
public function testDispatchUpdatesEventsCustom()
189274
{
190275
$user = new User();
@@ -231,6 +316,35 @@ public function testDispatchPropertyChangeEvents()
231316
$this->dispatcher->dispatchEvents();
232317
}
233318

319+
/**
320+
* Test that if dispatchEvents is called recursively (could happen if flush happens in a listener),
321+
* events already fired aren't a second time.
322+
*/
323+
public function testDispatchPropertyChangeEventsRecursive()
324+
{
325+
$user = new User();
326+
$annotation = new Change();
327+
$args = new LifecycleEventArgs($user, $this->objectManager);
328+
329+
$this->sfDispatcher->addSubscriber(new MySubscriber($this->dispatcher, $annotation, $args));
330+
331+
$this->dispatcher->addPropertyChange(
332+
$annotation,
333+
$user,
334+
'name',
335+
'foo',
336+
'bar'
337+
);
338+
339+
$this->assertCount(1, $this->dispatcher->getPropertyChanges());
340+
341+
// 2 === 1 addPropertyChange above + 1 addCreation in MySubscriber::onCalled
342+
$this->sfDispatcher->expects($this->exactly(2))
343+
->method('dispatch');
344+
345+
$this->dispatcher->dispatchEvents();
346+
}
347+
234348
public function testDispatchPropertyChangeEventsCustom()
235349
{
236350
$user = new User();
@@ -279,6 +393,37 @@ public function testDispatchCollectionChangeEvents()
279393
$this->dispatcher->dispatchEvents();
280394
}
281395

396+
/**
397+
* Test that if dispatchEvents is called recursively (could happen if flush happens in a listener),
398+
* events already fired aren't a second time.
399+
*/
400+
public function testDispatchCollectionChangeEventsRecursive()
401+
{
402+
$user = new User();
403+
$annotation = new Change();
404+
$args = new LifecycleEventArgs($user, $this->objectManager);
405+
406+
$this->sfDispatcher->addSubscriber(new MySubscriber($this->dispatcher, $annotation, $args));
407+
408+
$deleted = [new User()];
409+
$inserted = [new User(), new User()];
410+
$this->dispatcher->addCollectionChange(
411+
$annotation,
412+
$user,
413+
'friends',
414+
$deleted,
415+
$inserted
416+
);
417+
418+
$this->assertCount(1, $this->dispatcher->getCollectionChanges());
419+
420+
// 2 === 1 addCollectionChange above + 1 addCreation in MySubscriber::onCalled
421+
$this->sfDispatcher->expects($this->exactly(2))
422+
->method('dispatch');
423+
424+
$this->dispatcher->dispatchEvents();
425+
}
426+
282427
public function testDispatchCollectionChangeEventsCustom()
283428
{
284429
$user = new User();

0 commit comments

Comments
 (0)