Failed Conditions
Push — master ( bbe005...633c84 )
by Marco
136:20 queued 127:11
created

testWillNotMarkCollectionAsDirtyAfterInitializationIfNoElementsWereAdded()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 36
Code Lines 24

Duplication

Lines 36
Ratio 100 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 36
loc 36
rs 8.8571
c 1
b 0
f 0
cc 1
eloc 24
nc 1
nop 0
1
<?php
2
3
namespace Doctrine\Tests\ORM;
4
5
use Doctrine\Common\Collections\ArrayCollection;
6
use Doctrine\ORM\PersistentCollection;
7
use Doctrine\ORM\UnitOfWork;
8
use Doctrine\Tests\Mocks\ConnectionMock;
9
use Doctrine\Tests\Mocks\DriverMock;
10
use Doctrine\Tests\Mocks\EntityManagerMock;
11
use Doctrine\Tests\Models\ECommerce\ECommerceCart;
12
use Doctrine\Tests\Models\ECommerce\ECommerceProduct;
13
use Doctrine\Tests\OrmTestCase;
14
15
/**
16
 * Tests the lazy-loading capabilities of the PersistentCollection and the initialization of collections.
17
 * @author Giorgio Sironi <[email protected]>
18
 * @author Austin Morris <[email protected]>
19
 */
20
class PersistentCollectionTest extends OrmTestCase
21
{
22
    /**
23
     * @var PersistentCollection
24
     */
25
    protected $collection;
26
27
    /**
28
     * @var EntityManagerMock
29
     */
30
    private $_emMock;
31
32
    protected function setUp()
33
    {
34
        parent::setUp();
35
36
        $this->_emMock = EntityManagerMock::create(new ConnectionMock([], new DriverMock()));
37
38
        $this->setUpPersistentCollection();
39
    }
40
41
    /**
42
     * Set up the PersistentCollection used for collection initialization tests.
43
     */
44
    public function setUpPersistentCollection()
45
    {
46
        $classMetaData = $this->_emMock->getClassMetadata(ECommerceCart::class);
47
        $this->collection = new PersistentCollection($this->_emMock, $classMetaData, new ArrayCollection);
48
        $this->collection->setInitialized(false);
49
        $this->collection->setOwner(new ECommerceCart(), $classMetaData->getAssociationMapping('products'));
50
    }
51
52
    public function testCanBePutInLazyLoadingMode()
53
    {
54
        $class = $this->_emMock->getClassMetadata(ECommerceProduct::class);
55
        $collection = new PersistentCollection($this->_emMock, $class, new ArrayCollection);
56
        $collection->setInitialized(false);
57
        $this->assertFalse($collection->isInitialized());
58
    }
59
60
    /**
61
     * Test that PersistentCollection::current() initializes the collection.
62
     */
63
    public function testCurrentInitializesCollection()
64
    {
65
        $this->collection->current();
66
        $this->assertTrue($this->collection->isInitialized());
67
    }
68
69
    /**
70
     * Test that PersistentCollection::key() initializes the collection.
71
     */
72
    public function testKeyInitializesCollection()
73
    {
74
        $this->collection->key();
75
        $this->assertTrue($this->collection->isInitialized());
76
    }
77
78
    /**
79
     * Test that PersistentCollection::next() initializes the collection.
80
     */
81
    public function testNextInitializesCollection()
82
    {
83
        $this->collection->next();
84
        $this->assertTrue($this->collection->isInitialized());
85
    }
86
87
    /**
88
     * @group DDC-3382
89
     */
90
    public function testNonObjects()
91
    {
92
        $this->assertEmpty($this->collection);
93
94
        $this->collection->add("dummy");
95
96
        $this->assertNotEmpty($this->collection);
97
98
        $product = new ECommerceProduct();
99
100
        $this->collection->set(1, $product);
101
        $this->collection->set(2, "dummy");
102
        $this->collection->set(3, null);
103
104
        $this->assertSame($product, $this->collection->get(1));
105
        $this->assertSame("dummy", $this->collection->get(2));
106
        $this->assertSame(null, $this->collection->get(3));
107
    }
108
109
    /**
110
     * @group 6110
111
     */
112 View Code Duplication
    public function testRemovingElementsAlsoRemovesKeys()
113
    {
114
        $dummy = new \stdClass();
115
116
        $this->collection->add($dummy);
117
        $this->assertEquals([0], array_keys($this->collection->toArray()));
118
119
        $this->collection->removeElement($dummy);
120
        $this->assertEquals([], array_keys($this->collection->toArray()));
121
    }
122
123
    /**
124
     * @group 6110
125
     */
126
    public function testClearWillAlsoClearKeys()
127
    {
128
        $this->collection->add(new \stdClass());
129
        $this->collection->clear();
130
        $this->assertEquals([], array_keys($this->collection->toArray()));
131
    }
132
133
    /**
134
     * @group 6110
135
     */
136 View Code Duplication
    public function testClearWillAlsoResetKeyPositions()
137
    {
138
        $dummy = new \stdClass();
139
140
        $this->collection->add($dummy);
141
        $this->collection->removeElement($dummy);
142
        $this->collection->clear();
143
        $this->collection->add($dummy);
144
        $this->assertEquals([0], array_keys($this->collection->toArray()));
145
    }
146
147
    /**
148
     * @group 6613
149
     * @group 6614
150
     * @group 6616
151
     */
152 View Code Duplication
    public function testWillKeepNewItemsInDirtyCollectionAfterInitialization() : void
153
    {
154
        /* @var $unitOfWork UnitOfWork|\PHPUnit_Framework_MockObject_MockObject */
155
        $unitOfWork = $this->createMock(UnitOfWork::class);
156
157
        $this->_emMock->setUnitOfWork($unitOfWork);
0 ignored issues
show
Bug introduced by
It seems like $unitOfWork defined by $this->createMock(\Doctr...\ORM\UnitOfWork::class) on line 155 can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, Doctrine\Tests\Mocks\Ent...erMock::setUnitOfWork() does only seem to accept object<Doctrine\ORM\UnitOfWork>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
158
159
        $newElement       = new \stdClass();
160
        $persistedElement = new \stdClass();
161
162
        $this->collection->add($newElement);
163
164
        self::assertFalse($this->collection->isInitialized());
165
        self::assertTrue($this->collection->isDirty());
166
167
        $unitOfWork
0 ignored issues
show
Bug introduced by
The method expects does only exist in PHPUnit_Framework_MockObject_MockObject, but not in Doctrine\ORM\UnitOfWork.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
168
            ->expects(self::once())
169
            ->method('loadCollection')
170
            ->with($this->collection)
171
            ->willReturnCallback(function (PersistentCollection $persistentCollection) use ($persistedElement) : void {
172
                $persistentCollection->unwrap()->add($persistedElement);
173
            });
174
175
        $this->collection->initialize();
176
177
        self::assertSame([$persistedElement, $newElement], $this->collection->toArray());
178
        self::assertTrue($this->collection->isInitialized());
179
        self::assertTrue($this->collection->isDirty());
180
    }
181
182
    /**
183
     * @group 6613
184
     * @group 6614
185
     * @group 6616
186
     */
187
    public function testWillDeDuplicateNewItemsThatWerePreviouslyPersistedInDirtyCollectionAfterInitialization() : void
188
    {
189
        /* @var $unitOfWork UnitOfWork|\PHPUnit_Framework_MockObject_MockObject */
190
        $unitOfWork = $this->createMock(UnitOfWork::class);
191
192
        $this->_emMock->setUnitOfWork($unitOfWork);
0 ignored issues
show
Bug introduced by
It seems like $unitOfWork defined by $this->createMock(\Doctr...\ORM\UnitOfWork::class) on line 190 can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, Doctrine\Tests\Mocks\Ent...erMock::setUnitOfWork() does only seem to accept object<Doctrine\ORM\UnitOfWork>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
193
194
        $newElement                    = new \stdClass();
195
        $newElementThatIsAlsoPersisted = new \stdClass();
196
        $persistedElement              = new \stdClass();
197
198
        $this->collection->add($newElementThatIsAlsoPersisted);
199
        $this->collection->add($newElement);
200
201
        self::assertFalse($this->collection->isInitialized());
202
        self::assertTrue($this->collection->isDirty());
203
204
        $unitOfWork
0 ignored issues
show
Bug introduced by
The method expects does only exist in PHPUnit_Framework_MockObject_MockObject, but not in Doctrine\ORM\UnitOfWork.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
205
            ->expects(self::once())
206
            ->method('loadCollection')
207
            ->with($this->collection)
208
            ->willReturnCallback(function (PersistentCollection $persistentCollection) use (
209
                $persistedElement,
210
                $newElementThatIsAlsoPersisted
211
            ) : void {
212
                $persistentCollection->unwrap()->add($newElementThatIsAlsoPersisted);
213
                $persistentCollection->unwrap()->add($persistedElement);
214
            });
215
216
        $this->collection->initialize();
217
218
        self::assertSame(
219
            [$newElementThatIsAlsoPersisted, $persistedElement, $newElement],
220
            $this->collection->toArray()
221
        );
222
        self::assertTrue($this->collection->isInitialized());
223
        self::assertTrue($this->collection->isDirty());
224
    }
225
226
    /**
227
     * @group 6613
228
     * @group 6614
229
     * @group 6616
230
     */
231 View Code Duplication
    public function testWillNotMarkCollectionAsDirtyAfterInitializationIfNoElementsWereAdded() : void
232
    {
233
        /* @var $unitOfWork UnitOfWork|\PHPUnit_Framework_MockObject_MockObject */
234
        $unitOfWork = $this->createMock(UnitOfWork::class);
235
236
        $this->_emMock->setUnitOfWork($unitOfWork);
0 ignored issues
show
Bug introduced by
It seems like $unitOfWork defined by $this->createMock(\Doctr...\ORM\UnitOfWork::class) on line 234 can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, Doctrine\Tests\Mocks\Ent...erMock::setUnitOfWork() does only seem to accept object<Doctrine\ORM\UnitOfWork>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
237
238
        $newElementThatIsAlsoPersisted = new \stdClass();
239
        $persistedElement              = new \stdClass();
240
241
        $this->collection->add($newElementThatIsAlsoPersisted);
242
243
        self::assertFalse($this->collection->isInitialized());
244
        self::assertTrue($this->collection->isDirty());
245
246
        $unitOfWork
0 ignored issues
show
Bug introduced by
The method expects does only exist in PHPUnit_Framework_MockObject_MockObject, but not in Doctrine\ORM\UnitOfWork.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
247
            ->expects(self::once())
248
            ->method('loadCollection')
249
            ->with($this->collection)
250
            ->willReturnCallback(function (PersistentCollection $persistentCollection) use (
251
                $persistedElement,
252
                $newElementThatIsAlsoPersisted
253
            ) : void {
254
                $persistentCollection->unwrap()->add($newElementThatIsAlsoPersisted);
255
                $persistentCollection->unwrap()->add($persistedElement);
256
            });
257
258
        $this->collection->initialize();
259
260
        self::assertSame(
261
            [$newElementThatIsAlsoPersisted, $persistedElement],
262
            $this->collection->toArray()
263
        );
264
        self::assertTrue($this->collection->isInitialized());
265
        self::assertFalse($this->collection->isDirty());
266
    }
267
}
268