Completed
Pull Request — master (#634)
by Bene
06:45 queued 03:21
created

ReferenceListTest   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 394
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 12
Bugs 1 Features 4
Metric Value
wmc 44
c 12
b 1
f 4
lcom 1
cbo 6
dl 0
loc 394
rs 8.3396

34 Methods

Rating   Name   Duplication   Size   Complexity  
A instanceProvider() 0 9 2
A getElementInstances() 0 7 1
A getConstructorArg() 0 6 1
A testCanConstructWithReferenceListObject() 0 8 1
A testGivenInvalidConstructorArguments_constructorThrowsException() 0 3 1
A invalidConstructorArgumentsProvider() 0 18 1
A testHasReferenceBeforeRemoveButNotAfter() 0 15 3
A testRemoveReference() 0 23 2
A testAddReferenceOnEmptyList() 0 11 1
A assertSameReferenceOrder() 0 6 1
A testAddReferenceOnNonEmptyList() 0 13 1
A testAddReferenceAtIndexZero() 0 11 1
A testAddReferenceAtNegativeIndex() 0 7 1
A testGivenEmptyReference_addReferenceDoesNotAdd() 0 11 1
A testIndexOf() 0 8 2
A testGetIterator_isTraversable() 0 11 2
A testGivenCloneOfReferenceInList_hasReferenceReturnsTrue() 0 13 1
A testGivenEmptyReferenceAndIndex_addReferenceDoesNotAdd() 0 11 1
A testIndexOf_checksForIdentity() 0 10 1
A testEquals() 0 4 1
A testGetValueHashReturnsString() 0 3 1
A testGetValueHashIsTheSameForClone() 0 4 1
A testHasReferenceHash() 0 10 2
A testGetReference() 0 10 2
A testRemoveReferenceHash() 0 18 3
A testRemoveReferenceHash_occurrencesOfSameObjectGetRemoved() 0 14 1
A testGivenOneSnak_addNewReferenceAddsSnak() 0 7 1
A testGivenMultipleSnaks_addNewReferenceAddsThem() 0 11 1
A testGivenAnArrayOfSnaks_addNewReferenceAddsThem() 0 11 1
A testGivenNoneSnak_addNewReferenceThrowsException() 0 6 1
A testSerializationStability() 0 4 1
A testSerializeRoundtrip() 0 13 1
A testGivenEmptyList_isEmpty() 0 4 1
A testGivenNonEmptyList_isNotEmpty() 0 6 1

How to fix   Complexity   

Complex Class

Complex classes like ReferenceListTest often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ReferenceListTest, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Wikibase\DataModel\Tests;
4
5
use Hashable;
6
use InvalidArgumentException;
7
use PHPUnit_Framework_TestCase;
8
use Wikibase\DataModel\Entity\PropertyId;
9
use Wikibase\DataModel\Reference;
10
use Wikibase\DataModel\ReferenceList;
11
use Wikibase\DataModel\Snak\PropertyNoValueSnak;
12
use Wikibase\DataModel\Snak\SnakList;
13
14
/**
15
 * @covers Wikibase\DataModel\ReferenceList
16
 *
17
 * @group Wikibase
18
 * @group WikibaseDataModel
19
 * @group WikibaseReference
20
 *
21
 * @licence GNU GPL v2+
22
 * @author Jeroen De Dauw < [email protected] >
23
 * @author Thiemo Mättig
24
 */
25
class ReferenceListTest extends PHPUnit_Framework_TestCase {
26
27
	public function instanceProvider() {
28
		$instances = array();
29
30
		foreach ( $this->getConstructorArg() as $arg ) {
31
			$instances[] = array( new ReferenceList( $arg ) );
32
		}
33
34
		return $instances;
35
	}
36
37
	public function getElementInstances() {
38
		return array(
39
			new Reference(),
40
			new Reference( array( new PropertyNoValueSnak( 2 ) ) ),
41
			new Reference( array( new PropertyNoValueSnak( 3 ) ) ),
42
		);
43
	}
44
45
	public function getConstructorArg() {
46
		return array(
47
			array(),
48
			$this->getElementInstances(),
49
		);
50
	}
51
52
	public function testCanConstructWithReferenceListObject() {
53
		$reference = new Reference( array( new PropertyNoValueSnak( 1 ) ) );
54
		$original = new ReferenceList( array( $reference ) );
55
		$copy = new ReferenceList( $original );
56
57
		$this->assertSame( 1, $copy->count() );
58
		$this->assertNotNull( $copy->getReference( $reference->getHash() ) );
59
	}
60
61
	/**
62
	 * @dataProvider invalidConstructorArgumentsProvider
63
	 * @expectedException InvalidArgumentException
64
	 */
65
	public function testGivenInvalidConstructorArguments_constructorThrowsException( $input ) {
66
		new ReferenceList( $input );
67
	}
68
69
	public function invalidConstructorArgumentsProvider() {
70
		$id1 = new PropertyId( 'P1' );
71
72
		return array(
73
			array( null ),
74
			array( false ),
75
			array( 1 ),
76
			array( 0.1 ),
77
			array( 'string' ),
78
			array( $id1 ),
79
			array( new PropertyNoValueSnak( $id1 ) ),
80
			array( new Reference() ),
81
			array( new SnakList( array( new PropertyNoValueSnak( $id1 ) ) ) ),
82
			array( array( new PropertyNoValueSnak( $id1 ) ) ),
83
			array( array( new ReferenceList() ) ),
84
			array( array( new SnakList() ) ),
85
		);
86
	}
87
88
	public function testGetIterator_isTraversable() {
89
		$references = new ReferenceList();
90
		$references->addNewReference( new PropertyNoValueSnak( 1 ) );
91
		$iterator = $references->getIterator();
92
93
		$this->assertInstanceOf( 'Traversable', $iterator );
94
		$this->assertCount( 1, $iterator );
95
		foreach ( $references as $reference ) {
96
			$this->assertInstanceOf( 'Wikibase\DataModel\Reference', $reference );
97
		}
98
	}
99
100
	/**
101
	 * @dataProvider instanceProvider
102
	 */
103
	public function testHasReferenceBeforeRemoveButNotAfter( ReferenceList $array ) {
104
		if ( $array->count() === 0 ) {
105
			$this->assertTrue( true );
106
			return;
107
		}
108
109
		/**
110
		 * @var Reference $hashable
111
		 */
112
		foreach ( iterator_to_array( $array ) as $hashable ) {
113
			$this->assertTrue( $array->hasReference( $hashable ) );
114
			$array->removeReference( $hashable );
115
			$this->assertFalse( $array->hasReference( $hashable ) );
116
		}
117
	}
118
119
	public function testGivenCloneOfReferenceInList_hasReferenceReturnsTrue() {
120
		$list = new ReferenceList();
121
122
		$reference = new Reference( array( new PropertyNoValueSnak( 42 ) ) );
123
		$sameReference = unserialize( serialize( $reference ) );
124
125
		$list->addReference( $reference );
126
127
		$this->assertTrue(
128
			$list->hasReference( $sameReference ),
129
			'hasReference should return true when a reference with the same value is present, even when its another instance'
130
		);
131
	}
132
133
	/**
134
	 * @dataProvider instanceProvider
135
	 */
136
	public function testRemoveReference( ReferenceList $array ) {
137
		$elementCount = count( $array );
138
139
		/**
140
		 * @var Reference $element
141
		 */
142
		foreach ( iterator_to_array( $array ) as $element ) {
143
			$this->assertTrue( $array->hasReference( $element ) );
144
145
			$array->removeReference( $element );
146
147
			$this->assertFalse( $array->hasReference( $element ) );
148
			$this->assertEquals( --$elementCount, count( $array ) );
149
		}
150
151
		$elements = $this->getElementInstances();
152
		$element = array_shift( $elements );
153
154
		$array->removeReference( $element );
0 ignored issues
show
Bug introduced by
It seems like $element defined by array_shift($elements) on line 152 can be null; however, Wikibase\DataModel\Refer...List::removeReference() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
155
		$array->removeReference( $element );
0 ignored issues
show
Bug introduced by
It seems like $element defined by array_shift($elements) on line 152 can be null; however, Wikibase\DataModel\Refer...List::removeReference() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
156
157
		$this->assertTrue( true );
158
	}
159
160
	public function testAddReferenceOnEmptyList() {
161
		$reference = new Reference( array( new PropertyNoValueSnak( 1 ) ) );
162
163
		$references = new ReferenceList();
164
		$references->addReference( $reference );
165
166
		$this->assertCount( 1, $references );
167
168
		$expectedList = new ReferenceList( array( $reference ) );
169
		$this->assertSameReferenceOrder( $expectedList, $references );
170
	}
171
172
	private function assertSameReferenceOrder( ReferenceList $expectedList, ReferenceList $references ) {
173
		$this->assertEquals(
174
			iterator_to_array( $expectedList ),
175
			iterator_to_array( $references )
176
		);
177
	}
178
179
	public function testAddReferenceOnNonEmptyList() {
180
		$reference1 = new Reference( array( new PropertyNoValueSnak( 1 ) ) );
181
		$reference2 = new Reference( array( new PropertyNoValueSnak( 2 ) ) );
182
		$reference3 = new Reference( array( new PropertyNoValueSnak( 3 ) ) );
183
184
		$references = new ReferenceList( array( $reference1, $reference2 ) );
185
		$references->addReference( $reference3 );
186
187
		$this->assertCount( 3, $references );
188
189
		$expectedList = new ReferenceList( array( $reference1, $reference2, $reference3 ) );
190
		$this->assertSameReferenceOrder( $expectedList, $references );
191
	}
192
193
	public function testAddReferenceAtIndexZero() {
194
		$reference1 = new Reference( array( new PropertyNoValueSnak( 1 ) ) );
195
		$reference2 = new Reference( array( new PropertyNoValueSnak( 2 ) ) );
196
		$reference3 = new Reference( array( new PropertyNoValueSnak( 3 ) ) );
197
198
		$references = new ReferenceList( array( $reference1, $reference2 ) );
199
		$references->addReference( $reference3, 0 );
200
201
		$expectedList = new ReferenceList( array( $reference3, $reference1, $reference2 ) );
202
		$this->assertSameReferenceOrder( $expectedList, $references );
203
	}
204
205
	public function testAddReferenceAtNegativeIndex() {
206
		$reference = new Reference( array( new PropertyNoValueSnak( 1 ) ) );
207
		$referenceList = new ReferenceList();
208
209
		$this->setExpectedException( 'InvalidArgumentException' );
0 ignored issues
show
Deprecated Code introduced by
The method PHPUnit_Framework_TestCase::setExpectedException() has been deprecated with message: Method deprecated since Release 5.2.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
210
		$referenceList->addReference( $reference, -1 );
211
	}
212
213
	public function testGivenEmptyReference_addReferenceDoesNotAdd() {
214
		$reference1 = new Reference( array( new PropertyNoValueSnak( 1 ) ) );
215
		$reference2 = new Reference( array( new PropertyNoValueSnak( 2 ) ) );
216
		$emptyReference = new Reference( array() );
217
218
		$references = new ReferenceList( array( $reference1, $reference2 ) );
219
		$references->addReference( $emptyReference );
220
221
		$expectedList = new ReferenceList( array( $reference1, $reference2 ) );
222
		$this->assertSameReferenceOrder( $expectedList, $references );
223
	}
224
225
	public function testGivenEmptyReferenceAndIndex_addReferenceDoesNotAdd() {
226
		$reference1 = new Reference( array( new PropertyNoValueSnak( 1 ) ) );
227
		$reference2 = new Reference( array( new PropertyNoValueSnak( 2 ) ) );
228
		$emptyReference = new Reference( array() );
229
230
		$references = new ReferenceList( array( $reference1, $reference2 ) );
231
		$references->addReference( $emptyReference, 0 );
232
233
		$expectedList = new ReferenceList( array( $reference1, $reference2 ) );
234
		$this->assertSameReferenceOrder( $expectedList, $references );
235
	}
236
237
	/**
238
	 * @dataProvider instanceProvider
239
	 */
240
	public function testIndexOf( ReferenceList $array ) {
241
		$this->assertFalse( $array->indexOf( new Reference() ) );
242
243
		$i = 0;
244
		foreach ( $array as $reference ) {
245
			$this->assertEquals( $i++, $array->indexOf( $reference ) );
246
		}
247
	}
248
249
	public function testIndexOf_checksForIdentity() {
250
		$reference1 = new Reference( array( new PropertyNoValueSnak( 1 ) ) );
251
		$reference2 = new Reference( array( new PropertyNoValueSnak( 1 ) ) );
252
		$referenceList = new ReferenceList( array( $reference1 ) );
253
254
		$this->assertNotSame( $reference1, $reference2, 'post condition' );
255
		$this->assertTrue( $reference1->equals( $reference2 ), 'post condition' );
256
		$this->assertSame( 0, $referenceList->indexOf( $reference1 ), 'identity' );
257
		$this->assertFalse( $referenceList->indexOf( $reference2 ), 'not equality' );
258
	}
259
260
	/**
261
	 * @dataProvider instanceProvider
262
	 */
263
	public function testEquals( ReferenceList $array ) {
264
		$this->assertTrue( $array->equals( $array ) );
265
		$this->assertFalse( $array->equals( 42 ) );
266
	}
267
268
	/**
269
	 * @dataProvider instanceProvider
270
	 */
271
	public function testGetValueHashReturnsString( ReferenceList $array ) {
272
		$this->assertInternalType( 'string', $array->getValueHash() );
273
	}
274
275
	/**
276
	 * @dataProvider instanceProvider
277
	 */
278
	public function testGetValueHashIsTheSameForClone( ReferenceList $array ) {
279
		$copy = unserialize( serialize( $array ) );
280
		$this->assertEquals( $array->getValueHash(), $copy->getValueHash() );
281
	}
282
283
	/**
284
	 * @dataProvider instanceProvider
285
	 */
286
	public function testHasReferenceHash( ReferenceList $references ) {
287
		$this->assertFalse( $references->hasReferenceHash( '~=[,,_,,]:3' ) );
288
289
		/**
290
		 * @var Hashable $reference
291
		 */
292
		foreach ( $references as $reference ) {
293
			$this->assertTrue( $references->hasReferenceHash( $reference->getHash() ) );
294
		}
295
	}
296
297
	/**
298
	 * @dataProvider instanceProvider
299
	 */
300
	public function testGetReference( ReferenceList $references ) {
301
		$this->assertNull( $references->getReference( '~=[,,_,,]:3' ) );
302
303
		/**
304
		 * @var Reference $reference
305
		 */
306
		foreach ( $references as $reference ) {
307
			$this->assertTrue( $reference->equals( $references->getReference( $reference->getHash() ) ) );
308
		}
309
	}
310
311
	/**
312
	 * @dataProvider instanceProvider
313
	 */
314
	public function testRemoveReferenceHash( ReferenceList $references ) {
315
		$references->removeReferenceHash( '~=[,,_,,]:3' );
316
317
		$hashes = array();
318
319
		/**
320
		 * @var Reference $reference
321
		 */
322
		foreach ( $references as $reference ) {
323
			$hashes[] = $reference->getHash();
324
		}
325
326
		foreach ( $hashes as $hash ) {
327
			$references->removeReferenceHash( $hash );
328
		}
329
330
		$this->assertEquals( 0, count( $references ) );
331
	}
332
333
	public function testRemoveReferenceHash_occurrencesOfSameObjectGetRemoved() {
334
		$reference = new Reference( array( new PropertyNoValueSnak( 42 ) ) );
335
		$references = new ReferenceList( array(
336
			$reference,
337
			clone $reference,
338
			$reference
339
		) );
340
341
		$references->removeReferenceHash( $reference->getHash() );
342
343
		$this->assertTrue( $references->hasReferenceHash( $reference->getHash() ) );
344
		$this->assertCount( 1, $references );
345
		$this->assertNotSame( $reference, $references->getReference( $reference->getHash() ) );
346
	}
347
348
	public function testGivenOneSnak_addNewReferenceAddsSnak() {
349
		$references = new ReferenceList();
350
		$snak = new PropertyNoValueSnak( 1 );
351
352
		$references->addNewReference( $snak );
353
		$this->assertTrue( $references->hasReference( new Reference( array( $snak ) ) ) );
354
	}
355
356
	public function testGivenMultipleSnaks_addNewReferenceAddsThem() {
357
		$references = new ReferenceList();
358
		$snak1 = new PropertyNoValueSnak( 1 );
359
		$snak2 = new PropertyNoValueSnak( 3 );
360
		$snak3 = new PropertyNoValueSnak( 2 );
361
362
		$references->addNewReference( $snak1, $snak2, $snak3 );
363
364
		$expectedSnaks = array( $snak1, $snak2, $snak3 );
365
		$this->assertTrue( $references->hasReference( new Reference( $expectedSnaks ) ) );
366
	}
367
368
	public function testGivenAnArrayOfSnaks_addNewReferenceAddsThem() {
369
		$references = new ReferenceList();
370
		$snaks = array(
371
			new PropertyNoValueSnak( 1 ),
372
			new PropertyNoValueSnak( 3 ),
373
			new PropertyNoValueSnak( 2 )
374
		);
375
376
		$references->addNewReference( $snaks );
377
		$this->assertTrue( $references->hasReference( new Reference( $snaks ) ) );
378
	}
379
380
	public function testGivenNoneSnak_addNewReferenceThrowsException() {
381
		$references = new ReferenceList();
382
383
		$this->setExpectedException( 'InvalidArgumentException' );
0 ignored issues
show
Deprecated Code introduced by
The method PHPUnit_Framework_TestCase::setExpectedException() has been deprecated with message: Method deprecated since Release 5.2.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
384
		$references->addNewReference( new PropertyNoValueSnak( 1 ), null );
385
	}
386
387
	public function testSerializationStability() {
388
		$references = new ReferenceList();
389
		$this->assertSame( 'a:0:{}', $references->serialize() );
390
	}
391
392
	public function testSerializeRoundtrip() {
393
		$references = new ReferenceList();
394
395
		$references->addReference( new Reference() );
396
397
		$references->addReference( new Reference( array(
398
			new PropertyNoValueSnak( 2 ),
399
			new PropertyNoValueSnak( 3 ),
400
		) ) );
401
402
		$serialized = serialize( $references );
403
		$this->assertTrue( $references->equals( unserialize( $serialized ) ) );
404
	}
405
406
	public function testGivenEmptyList_isEmpty() {
407
		$references = new ReferenceList();
408
		$this->assertTrue( $references->isEmpty() );
409
	}
410
411
	public function testGivenNonEmptyList_isNotEmpty() {
412
		$references = new ReferenceList();
413
		$references->addNewReference( new PropertyNoValueSnak( 1 ) );
414
415
		$this->assertFalse( $references->isEmpty() );
416
	}
417
418
}
419