Test Setup Failed
Push — T243590 ( 74daf7 )
by
unknown
02:12
created

testCanConstructWithReferenceListObject()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace Wikibase\DataModel\Tests;
4
5
use Hashable;
6
use InvalidArgumentException;
7
use Traversable;
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
 * @license GPL-2.0-or-later
22
 * @author Jeroen De Dauw < [email protected] >
23
 * @author Thiemo Kreuz
24
 */
25
class ReferenceListTest extends \PHPUnit\Framework\TestCase {
26
27
	public function instanceProvider() {
28
		return [
29
			[ new ReferenceList( [] ) ],
30
			[ new ReferenceList( [
31
				new Reference(),
32
				new Reference( [ new PropertyNoValueSnak( 2 ) ] ),
33
				new Reference( [ new PropertyNoValueSnak( 3 ) ] ),
34
			] ) ],
35
		];
36
	}
37
38
39
	// TODO
40
	// - add stability test for referencelist -> getValueHashes
41
	// ! add function comments
42
	// - create pr
43
	// - write thiemo + adam ask
44
	//		- is it ok for the schema to change?
45
	//		- is it used?
46
	// 		- should we remove it?
47
	// 		- https://wiki.php.net/rfc/custom_object_serialization
48
49
50
51
	public function testCanConstructWithReferenceListObject() {
52
		$reference = new Reference( [ new PropertyNoValueSnak( 1 ) ] );
53
		$original = new ReferenceList( [ $reference ] );
54
		$copy = new ReferenceList( $original );
55
56
		$this->assertSame( 1, $copy->count() );
57
		$this->assertNotNull( $copy->getReference( $reference->getHash() ) );
58
	}
59
60
	public function testConstructorIgnoresIdenticalObjects() {
61
		$reference = new Reference( [ new PropertyNoValueSnak( 1 ) ] );
62
		$list = new ReferenceList( [ $reference, $reference ] );
63
		$this->assertCount( 1, $list );
64
	}
65
66
	public function testConstructorDoesNotIgnoreCopies() {
67
		$reference = new Reference( [ new PropertyNoValueSnak( 1 ) ] );
68
		$list = new ReferenceList( [ $reference, clone $reference ] );
69
		$this->assertCount( 2, $list );
70
	}
71
72
	/**
73
	 * @dataProvider invalidConstructorArgumentsProvider
74
	 */
75
	public function testGivenInvalidConstructorArguments_constructorThrowsException( $input ) {
76
		$this->expectException( InvalidArgumentException::class );
77
		new ReferenceList( $input );
78
	}
79
80
	public function invalidConstructorArgumentsProvider() {
81
		$id1 = new PropertyId( 'P1' );
82
83
		return [
84
			[ null ],
85
			[ false ],
86
			[ 1 ],
87
			[ 0.1 ],
88
			[ 'string' ],
89
			[ $id1 ],
90
			[ new PropertyNoValueSnak( $id1 ) ],
91
			[ new Reference() ],
92
			[ new SnakList( [ new PropertyNoValueSnak( $id1 ) ] ) ],
93
			[ [ new PropertyNoValueSnak( $id1 ) ] ],
94
			[ [ new ReferenceList() ] ],
95
			[ [ new SnakList() ] ],
96
		];
97
	}
98
99
	public function testGetIterator_isTraversable() {
100
		$references = new ReferenceList();
101
		$references->addNewReference( new PropertyNoValueSnak( 1 ) );
102
		$iterator = $references->getIterator();
103
104
		$this->assertInstanceOf( Traversable::class, $iterator );
105
		$this->assertCount( 1, $iterator );
106
		foreach ( $references as $reference ) {
107
			$this->assertInstanceOf( Reference::class, $reference );
108
		}
109
	}
110
111
	/**
112
	 * @dataProvider instanceProvider
113
	 */
114
	public function testHasReferenceBeforeRemoveButNotAfter( ReferenceList $array ) {
115
		if ( $array->count() === 0 ) {
116
			$this->assertTrue( true );
117
			return;
118
		}
119
120
		/**
121
		 * @var Reference $hashable
122
		 */
123
		foreach ( iterator_to_array( $array ) as $hashable ) {
124
			$this->assertTrue( $array->hasReference( $hashable ) );
125
			$array->removeReference( $hashable );
126
			$this->assertFalse( $array->hasReference( $hashable ) );
127
		}
128
	}
129
130
	public function testGivenCloneOfReferenceInList_hasReferenceReturnsTrue() {
131
		$list = new ReferenceList();
132
133
		$reference = new Reference( [ new PropertyNoValueSnak( 42 ) ] );
134
		$sameReference = unserialize( serialize( $reference ) );
135
136
		$list->addReference( $reference );
137
138
		$this->assertTrue(
139
			$list->hasReference( $sameReference ),
140
			'hasReference should return true when a reference with the same value is present, even '
141
				. 'when its another instance'
142
		);
143
	}
144
145
	/**
146
	 * @dataProvider instanceProvider
147
	 */
148
	public function testRemoveReference( ReferenceList $array ) {
149
		$elementCount = count( $array );
150
151
		/**
152
		 * @var Reference $element
153
		 */
154
		foreach ( iterator_to_array( $array ) as $element ) {
155
			$this->assertTrue( $array->hasReference( $element ) );
156
157
			$array->removeReference( $element );
158
159
			$this->assertFalse( $array->hasReference( $element ) );
160
			$this->assertSame( --$elementCount, count( $array ) );
161
		}
162
		if ( $elementCount === 0 ) {
163
			$this->assertTrue( true );
164
		}
165
	}
166
167
	public function testRemoveReferenceRemovesIdenticalObjects() {
168
		$reference = new Reference( [ new PropertyNoValueSnak( 1 ) ] );
169
		$references = new ReferenceList( [ $reference, $reference ] );
170
171
		$references->removeReference( $reference );
172
173
		$this->assertTrue( $references->isEmpty() );
174
	}
175
176
	public function testRemoveReferenceDoesNotRemoveCopies() {
177
		$reference = new Reference( [ new PropertyNoValueSnak( 1 ) ] );
178
		$references = new ReferenceList( [ $reference, clone $reference ] );
179
180
		$references->removeReference( $reference );
181
182
		$this->assertFalse( $references->isEmpty() );
183
		$this->assertTrue( $references->hasReference( $reference ) );
184
		$this->assertNotSame( $reference, $references->getReference( $reference->getHash() ) );
185
	}
186
187
	public function testAddReferenceOnEmptyList() {
188
		$reference = new Reference( [ new PropertyNoValueSnak( 1 ) ] );
189
190
		$references = new ReferenceList();
191
		$references->addReference( $reference );
192
193
		$this->assertCount( 1, $references );
194
195
		$expectedList = new ReferenceList( [ $reference ] );
196
		$this->assertSameReferenceOrder( $expectedList, $references );
197
	}
198
199
	private function assertSameReferenceOrder( ReferenceList $expectedList, ReferenceList $references ) {
200
		$this->assertSame(
201
			iterator_to_array( $expectedList ),
202
			iterator_to_array( $references )
203
		);
204
	}
205
206
	public function testAddReferenceAtTheEnd() {
207
		$reference1 = new Reference( [ new PropertyNoValueSnak( 1 ) ] );
208
		$reference2 = new Reference( [ new PropertyNoValueSnak( 2 ) ] );
209
		$reference3 = new Reference( [ new PropertyNoValueSnak( 3 ) ] );
210
211
		$references = new ReferenceList( [ $reference1, $reference2 ] );
212
		$references->addReference( $reference3 );
213
214
		$this->assertCount( 3, $references );
215
216
		$expectedList = new ReferenceList( [ $reference1, $reference2, $reference3 ] );
217
		$this->assertSameReferenceOrder( $expectedList, $references );
218
	}
219
220
	public function testAddReferenceBetweenExistingReferences() {
221
		$reference1 = new Reference( [ new PropertyNoValueSnak( 1 ) ] );
222
		$reference2 = new Reference( [ new PropertyNoValueSnak( 2 ) ] );
223
		$list = new ReferenceList( [ $reference1, $reference2 ] );
224
225
		$reference3 = new Reference( [ new PropertyNoValueSnak( 3 ) ] );
226
		$list->addReference( $reference3, 1 );
227
228
		$this->assertCount( 3, $list );
229
		$this->assertSame( 1, $list->indexOf( $reference3 ) );
230
	}
231
232
	public function testAddReferenceIgnoresIdenticalObjects() {
233
		$list = new ReferenceList();
234
		$reference = new Reference( [ new PropertyNoValueSnak( 1 ) ] );
235
		$list->addReference( $reference );
236
		$list->addReference( $reference );
237
		$this->assertCount( 1, $list );
238
	}
239
240
	public function testAddReferenceDoesNotIgnoreCopies() {
241
		$list = new ReferenceList();
242
		$reference = new Reference( [ new PropertyNoValueSnak( 1 ) ] );
243
		$list->addReference( $reference );
244
		$list->addReference( clone $reference );
245
		$this->assertCount( 2, $list );
246
	}
247
248
	public function testAddReferenceAtIndexIgnoresIdenticalObjects() {
249
		$list = new ReferenceList();
250
		$reference = new Reference( [ new PropertyNoValueSnak( 1 ) ] );
251
		$list->addReference( $reference, 0 );
252
		$list->addReference( $reference, 0 );
253
		$this->assertCount( 1, $list );
254
	}
255
256
	public function testAddReferenceAtIndexMovesIdenticalObjects() {
257
		$list = new ReferenceList();
258
		$list->addNewReference( new PropertyNoValueSnak( 1 ) );
259
		$reference = new Reference( [ new PropertyNoValueSnak( 2 ) ] );
260
		$list->addReference( $reference );
261
		$list->addNewReference( new PropertyNoValueSnak( 3 ) );
262
263
		$this->assertSame(
264
			1,
265
			$list->indexOf( $reference ),
266
			'pre-condition is that the element is at index 1'
267
		);
268
269
		$list->addReference( $reference, 0 );
270
271
		$this->assertCount( 3, $list, 'not added' );
272
		$this->assertSame(
273
			1,
274
			$list->indexOf( $reference ),
275
			'make sure calling addReference with a lower index did not changed it'
276
		);
277
278
		$list->addReference( $reference, 2 );
279
280
		$this->assertCount( 3, $list, 'not added' );
281
		$this->assertSame(
282
			1,
283
			$list->indexOf( $reference ),
284
			'make sure calling addReference with a higher index did not changed it'
285
		);
286
	}
287
288
	public function testAddReferenceAtIndexZero() {
289
		$reference1 = new Reference( [ new PropertyNoValueSnak( 1 ) ] );
290
		$reference2 = new Reference( [ new PropertyNoValueSnak( 2 ) ] );
291
		$reference3 = new Reference( [ new PropertyNoValueSnak( 3 ) ] );
292
293
		$references = new ReferenceList( [ $reference1, $reference2 ] );
294
		$references->addReference( $reference3, 0 );
295
296
		$expectedList = new ReferenceList( [ $reference3, $reference1, $reference2 ] );
297
		$this->assertSameReferenceOrder( $expectedList, $references );
298
	}
299
300
	public function testAddReferenceAtNegativeIndex() {
301
		$reference = new Reference( [ new PropertyNoValueSnak( 1 ) ] );
302
		$referenceList = new ReferenceList();
303
304
		$this->expectException( InvalidArgumentException::class );
305
		$referenceList->addReference( $reference, -1 );
306
	}
307
308
	public function testGivenEmptyReference_addReferenceDoesNotAdd() {
309
		$reference1 = new Reference( [ new PropertyNoValueSnak( 1 ) ] );
310
		$reference2 = new Reference( [ new PropertyNoValueSnak( 2 ) ] );
311
		$emptyReference = new Reference( [] );
312
313
		$references = new ReferenceList( [ $reference1, $reference2 ] );
314
		$references->addReference( $emptyReference );
315
316
		$expectedList = new ReferenceList( [ $reference1, $reference2 ] );
317
		$this->assertSameReferenceOrder( $expectedList, $references );
318
	}
319
320
	public function testGivenEmptyReferenceAndIndex_addReferenceDoesNotAdd() {
321
		$reference1 = new Reference( [ new PropertyNoValueSnak( 1 ) ] );
322
		$reference2 = new Reference( [ new PropertyNoValueSnak( 2 ) ] );
323
		$emptyReference = new Reference( [] );
324
325
		$references = new ReferenceList( [ $reference1, $reference2 ] );
326
		$references->addReference( $emptyReference, 0 );
327
328
		$expectedList = new ReferenceList( [ $reference1, $reference2 ] );
329
		$this->assertSameReferenceOrder( $expectedList, $references );
330
	}
331
332
	/**
333
	 * @dataProvider instanceProvider
334
	 */
335
	public function testIndexOf( ReferenceList $array ) {
336
		$this->assertFalse( $array->indexOf( new Reference() ) );
337
338
		$i = 0;
339
		foreach ( $array as $reference ) {
340
			$this->assertSame( $i++, $array->indexOf( $reference ) );
341
		}
342
	}
343
344
	public function testIndexOf_checksForIdentity() {
345
		$reference1 = new Reference( [ new PropertyNoValueSnak( 1 ) ] );
346
		$reference2 = new Reference( [ new PropertyNoValueSnak( 1 ) ] );
347
		$referenceList = new ReferenceList( [ $reference1 ] );
348
349
		$this->assertNotSame( $reference1, $reference2, 'post condition' );
350
		$this->assertTrue( $reference1->equals( $reference2 ), 'post condition' );
351
		$this->assertSame( 0, $referenceList->indexOf( $reference1 ), 'identity' );
352
		$this->assertFalse( $referenceList->indexOf( $reference2 ), 'not equality' );
353
	}
354
355
	/**
356
	 * @dataProvider instanceProvider
357
	 */
358
	public function testEquals( ReferenceList $array ) {
359
		$this->assertTrue( $array->equals( $array ) );
360
		$this->assertFalse( $array->equals( 42 ) );
361
	}
362
363
	/**
364
	 * @dataProvider instanceProvider
365
	 */
366
	public function testGetValueHashReturnsString( ReferenceList $array ) {
367
		$this->assertIsString( $array->getValueHash() );
368
	}
369
370
	/**
371
	 * @dataProvider instanceProvider
372
	 */
373
	public function testGetValueHashIsTheSameForClone( ReferenceList $array ) {
374
		$copy = unserialize( serialize( $array ) );
375
		$this->assertSame( $array->getValueHash(), $copy->getValueHash() );
376
	}
377
378
	/**
379
	 * @dataProvider instanceProvider
380
	 */
381
	public function testHasReferenceHash( ReferenceList $references ) {
382
		$this->assertFalse( $references->hasReferenceHash( '~=[,,_,,]:3' ) );
383
384
		/**
385
		 * @var Hashable $reference
386
		 */
387
		foreach ( $references as $reference ) {
388
			$this->assertTrue( $references->hasReferenceHash( $reference->getHash() ) );
389
		}
390
	}
391
392
	/**
393
	 * @dataProvider instanceProvider
394
	 */
395
	public function testGetReference( ReferenceList $references ) {
396
		$this->assertNull( $references->getReference( '~=[,,_,,]:3' ) );
397
398
		/**
399
		 * @var Reference $reference
400
		 */
401
		foreach ( $references as $reference ) {
402
			$this->assertTrue( $reference->equals( $references->getReference( $reference->getHash() ) ) );
403
		}
404
	}
405
406
	/**
407
	 * @dataProvider instanceProvider
408
	 */
409
	public function testRemoveReferenceHash( ReferenceList $references ) {
410
		$references->removeReferenceHash( '~=[,,_,,]:3' );
411
412
		$hashes = [];
413
414
		/**
415
		 * @var Reference $reference
416
		 */
417
		foreach ( $references as $reference ) {
418
			$hashes[] = $reference->getHash();
419
		}
420
421
		foreach ( $hashes as $hash ) {
422
			$references->removeReferenceHash( $hash );
423
		}
424
425
		$this->assertTrue( $references->isEmpty() );
426
	}
427
428
	/**
429
	 * This integration test (relies on ReferenceList::getValueHash) is supposed to break whenever the hash
430
	 * calculation changes.
431
	 */
432
	public function testGetValueHashStability() {
433
		$array = new ReferenceList();
434
		$snak1 = new PropertyNoValueSnak( 1 );
435
		$snak2 = new PropertyNoValueSnak( 3 );
436
		$snak3 = new PropertyNoValueSnak( 2 );
437
438
		$array->addNewReference( $snak1, $snak2, $snak3 );
439
		$expectedHash = '92244e1a91f60b7fa922d42441995442bf50adb5';
440
		$this->assertSame( $expectedHash, $array->getValueHash());
441
	}
442
443
	public function testRemoveReferenceHashRemovesIdenticalObjects() {
444
		$reference = new Reference( [ new PropertyNoValueSnak( 1 ) ] );
445
		$references = new ReferenceList( [ $reference, $reference ] );
446
447
		$references->removeReferenceHash( $reference->getHash() );
448
449
		$this->assertTrue( $references->isEmpty() );
450
	}
451
452
	public function testRemoveReferenceHashDoesNotRemoveCopies() {
453
		$reference = new Reference( [ new PropertyNoValueSnak( 1 ) ] );
454
		$references = new ReferenceList( [ $reference, clone $reference ] );
455
456
		$references->removeReferenceHash( $reference->getHash() );
457
458
		$this->assertFalse( $references->isEmpty() );
459
		$this->assertTrue( $references->hasReference( $reference ) );
460
		$this->assertNotSame( $reference, $references->getReference( $reference->getHash() ) );
461
	}
462
463
	public function testRemoveReferenceHashUpdatesIndexes() {
464
		$reference1 = new Reference( [ new PropertyNoValueSnak( 1 ) ] );
465
		$reference2 = new Reference( [ new PropertyNoValueSnak( 2 ) ] );
466
		$references = new ReferenceList( [ $reference1, $reference2 ] );
467
468
		$references->removeReferenceHash( $reference1->getHash() );
469
470
		$this->assertSame( 0, $references->indexOf( $reference2 ) );
471
	}
472
473
	public function testGivenOneSnak_addNewReferenceAddsSnak() {
474
		$references = new ReferenceList();
475
		$snak = new PropertyNoValueSnak( 1 );
476
477
		$references->addNewReference( $snak );
478
		$this->assertTrue( $references->hasReference( new Reference( [ $snak ] ) ) );
479
	}
480
481
	public function testGivenMultipleSnaks_addNewReferenceAddsThem() {
482
		$references = new ReferenceList();
483
		$snak1 = new PropertyNoValueSnak( 1 );
484
		$snak2 = new PropertyNoValueSnak( 3 );
485
		$snak3 = new PropertyNoValueSnak( 2 );
486
487
		$references->addNewReference( $snak1, $snak2, $snak3 );
488
489
		$expectedSnaks = [ $snak1, $snak2, $snak3 ];
490
		$this->assertTrue( $references->hasReference( new Reference( $expectedSnaks ) ) );
491
	}
492
493
	public function testGivenAnArrayOfSnaks_addNewReferenceAddsThem() {
494
		$references = new ReferenceList();
495
		$snaks = [
496
			new PropertyNoValueSnak( 1 ),
497
			new PropertyNoValueSnak( 3 ),
498
			new PropertyNoValueSnak( 2 )
499
		];
500
501
		$references->addNewReference( $snaks );
502
		$this->assertTrue( $references->hasReference( new Reference( $snaks ) ) );
503
	}
504
505
	public function testAddNewReferenceDoesNotIgnoreIdenticalObjects() {
506
		$list = new ReferenceList();
507
		$snak = new PropertyNoValueSnak( 1 );
508
		$list->addNewReference( $snak );
509
		$list->addNewReference( $snak );
510
		$this->assertCount( 2, $list );
511
	}
512
513
	public function testAddNewReferenceDoesNotIgnoreCopies() {
514
		$list = new ReferenceList();
515
		$snak = new PropertyNoValueSnak( 1 );
516
		$list->addNewReference( $snak );
517
		$list->addNewReference( clone $snak );
518
		$this->assertCount( 2, $list );
519
	}
520
521
	public function testGivenNoneSnak_addNewReferenceThrowsException() {
522
		$references = new ReferenceList();
523
524
		$this->expectException( InvalidArgumentException::class );
525
		$references->addNewReference( new PropertyNoValueSnak( 1 ), null );
526
	}
527
528
	public function testEmptySerializationStability() {
529
		$list = new ReferenceList();
530
		$this->assertSame( 'a:0:{}', $list->serialize() );
531
	}
532
533
	public function testSerializationStability() {
534
		$list = new ReferenceList();
535
		$list->addNewReference( new PropertyNoValueSnak( 1 ) );
536
537
		$testString = "a:1:{i:0;O:28:\"Wikibase\\DataModel\\Reference\":1:{s:35:\"\x00Wikibase\\DataModel\\"
538
			. "Reference\x00snaks\";C:32:\"Wikibase\\DataModel\\Snak\\SnakList\":100:{a:2:{s:4:\""
539
			. 'data";a:1:{i:0;C:43:"Wikibase\\DataModel\\Snak\\PropertyNoValueSnak":2:{P1}}s:5'
540
			. ':"index";i:0;}}}}';
541
542
		$secondList = new ReferenceList();
543
		$secondList->unserialize($testString);
544
545
		$this->assertSame(
546
			$testString,
547
			$list->serialize()
548
		);
549
	}
550
551
	public function testSerializeUnserializeRoundtrip() {
552
		$original = new ReferenceList();
553
		$original->addNewReference( new PropertyNoValueSnak( 1 ) );
554
555
		/** @var ReferenceList $clone */
556
		$clone = unserialize( serialize( $original ) );
557
558
		$this->assertTrue( $original->equals( $clone ) );
559
		$this->assertSame( $original->getValueHash(), $clone->getValueHash() );
560
		$this->assertSame( $original->serialize(), $clone->serialize() );
561
	}
562
563
	public function testUnserializeCreatesNonIdenticalClones() {
564
		$original = new ReferenceList();
565
		$reference = new Reference( [ new PropertyNoValueSnak( 1 ) ] );
566
		$original->addReference( $reference );
567
568
		/** @var ReferenceList $clone */
569
		$clone = unserialize( serialize( $original ) );
570
		$clone->addReference( $reference );
571
572
		$this->assertCount( 2, $clone );
573
	}
574
575
	public function testGivenEmptyList_isEmpty() {
576
		$references = new ReferenceList();
577
		$this->assertTrue( $references->isEmpty() );
578
	}
579
580
	public function testGivenNonEmptyList_isNotEmpty() {
581
		$references = new ReferenceList();
582
		$references->addNewReference( new PropertyNoValueSnak( 1 ) );
583
584
		$this->assertFalse( $references->isEmpty() );
585
	}
586
587
}
588