Completed
Pull Request — 7.x (#767)
by no
05:10 queued 02:29
created

testGivenEqualSnakLists_getHashIsTheSame()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 2
1
<?php
2
3
namespace Wikibase\DataModel\Tests\Snak;
4
5
use Comparable;
6
use DataValues\StringValue;
7
use Hashable;
8
use InvalidArgumentException;
9
use Wikibase\DataModel\Entity\PropertyId;
10
use Wikibase\DataModel\Snak\PropertyNoValueSnak;
11
use Wikibase\DataModel\Snak\PropertyValueSnak;
12
use Wikibase\DataModel\Snak\Snak;
13
use Wikibase\DataModel\Snak\SnakList;
14
use Wikibase\DataModel\Tests\HashArray\HashArrayTest;
15
16
/**
17
 * @covers Wikibase\DataModel\Snak\SnakList
18
 * @uses DataValues\StringValue
19
 * @uses Wikibase\DataModel\Entity\PropertyId
20
 * @uses Wikibase\DataModel\Snak\PropertyNoValueSnak
21
 * @uses Wikibase\DataModel\Snak\PropertyValueSnak
22
 * @uses Wikibase\DataModel\Snak\Snak
23
 * @uses Wikibase\DataModel\Snak\SnakList
24
 * @uses Wikibase\DataModel\HashArray
25
 * @uses Wikibase\DataModel\Snak\SnakObject
26
 * @uses Wikibase\DataModel\Internal\MapValueHasher
27
 * @uses Wikibase\DataModel\Entity\EntityId
28
 *
29
 * @license GPL-2.0+
30
 * @author Jeroen De Dauw < [email protected] >
31
 * @author Addshore
32
 * @author Thiemo Mättig
33
 */
34
class SnakListTest extends HashArrayTest {
35
36
	public function elementInstancesProvider() {
37
		$id42 = new PropertyId( 'P42' );
38
39
		$argLists = [];
40
41
		$argLists[] = [ [ new PropertyNoValueSnak( $id42 ) ] ];
42
		$argLists[] = [ [ new PropertyNoValueSnak( new PropertyId( 'P9001' ) ) ] ];
43
		$argLists[] = [ [ new PropertyValueSnak( $id42, new StringValue( 'a' ) ) ] ];
44
45
		return $argLists;
46
	}
47
48
	public function instanceProvider() {
49
		$id42 = new PropertyId( 'P42' );
50
		$id9001 = new PropertyId( 'P9001' );
51
52
		return [
53
			[ new SnakList() ],
54
			[ new SnakList( [
55
				new PropertyNoValueSnak( $id42 )
56
			] ) ],
57
			[ new SnakList( [
58
				new PropertyNoValueSnak( $id42 ),
59
				new PropertyNoValueSnak( $id9001 ),
60
			] ) ],
61
			[ new SnakList( [
62
				new PropertyNoValueSnak( $id42 ),
63
				new PropertyNoValueSnak( $id9001 ),
64
				new PropertyValueSnak( $id42, new StringValue( 'a' ) ),
65
			] ) ],
66
		];
67
	}
68
69
	/**
70
	 * @dataProvider invalidConstructorArgumentsProvider
71
	 * @expectedException InvalidArgumentException
72
	 */
73
	public function testGivenInvalidConstructorArguments_constructorThrowsException( $input ) {
74
		new SnakList( $input );
75
	}
76
77
	public function invalidConstructorArgumentsProvider() {
78
		$id1 = new PropertyId( 'P1' );
79
80
		return [
81
			[ null ],
82
			[ false ],
83
			[ 1 ],
84
			[ 0.1 ],
85
			[ 'string' ],
86
			[ $id1 ],
87
			[ new PropertyNoValueSnak( $id1 ) ],
88
			[ new PropertyValueSnak( $id1, new StringValue( 'a' ) ) ],
89
			[ [ null ] ],
90
			[ [ $id1 ] ],
91
			[ [ new SnakList() ] ],
92
		];
93
	}
94
95
	public function testGivenAssociativeArray_constructorPreservesArrayKeys() {
96
		$snakList = new SnakList( [ 'key' => new PropertyNoValueSnak( 1 ) ] );
97
		$this->assertSame( [ 'key' ], array_keys( iterator_to_array( $snakList ) ) );
98
	}
99
100
	/**
101
	 * @dataProvider instanceProvider
102
	 * @param SnakList $array
103
	 */
104
	public function testHasSnak( SnakList $array ) {
105
		/**
106
		 * @var Snak $hashable
107
		 */
108
		foreach ( iterator_to_array( $array ) as $hashable ) {
109
			$this->assertTrue( $array->hasSnak( $hashable ) );
110
			$this->assertTrue( $array->hasSnakHash( $hashable->getHash() ) );
111
			$array->removeSnak( $hashable );
112
			$this->assertFalse( $array->hasSnak( $hashable ) );
113
			$this->assertFalse( $array->hasSnakHash( $hashable->getHash() ) );
114
		}
115
116
		$this->assertTrue( true );
117
	}
118
119
	/**
120
	 * @dataProvider instanceProvider
121
	 * @param SnakList $array
122
	 */
123
	public function testRemoveSnak( SnakList $array ) {
124
		$elementCount = $array->count();
125
126
		/**
127
		 * @var Snak $element
128
		 */
129
		foreach ( iterator_to_array( $array ) as $element ) {
130
			$this->assertTrue( $array->hasSnak( $element ) );
131
132
			if ( $elementCount % 2 === 0 ) {
133
				$array->removeSnak( $element );
134
			} else {
135
				$array->removeSnakHash( $element->getHash() );
136
			}
137
138
			$this->assertFalse( $array->hasSnak( $element ) );
139
			$this->assertEquals( --$elementCount, $array->count() );
140
		}
141
142
		$element = new PropertyNoValueSnak( new PropertyId( 'P42' ) );
143
144
		$array->removeSnak( $element );
145
		$array->removeSnakHash( $element->getHash() );
146
147
		$this->assertTrue( true );
148
	}
149
150
	/**
151
	 * @dataProvider instanceProvider
152
	 * @param SnakList $array
153
	 */
154
	public function testAddSnak( SnakList $array ) {
155
		$elementCount = $array->count();
156
157
		$elements = $this->elementInstancesProvider();
158
		$element = array_shift( $elements );
159
		$element = $element[0][0];
160
161
		if ( !$array->hasSnak( $element ) ) {
162
			++$elementCount;
163
		}
164
165
		$this->assertEquals( !$array->hasSnak( $element ), $array->addSnak( $element ) );
166
167
		$this->assertEquals( $elementCount, $array->count() );
168
169
		$this->assertFalse( $array->addSnak( $element ) );
170
171
		$this->assertEquals( $elementCount, $array->count() );
172
	}
173
174
	public function orderByPropertyProvider() {
175
		$id1 = new PropertyId( 'P1' );
176
		$id2 = new PropertyId( 'P2' );
177
		$id3 = new PropertyId( 'P3' );
178
		$id4 = new PropertyId( 'P4' );
179
180
		/**
181
		 * List of test data containing snaks to initialize SnakList objects. The first list of
182
		 * snaks represents the snak list to be used as test input while the second represents the
183
		 * expected result.
184
		 * @var array
185
		 */
186
		$rawArguments = [
187
			'Default order' => [
188
				[],
189
				[],
190
			],
191
			'Unknown id in order' => [
192
				[],
193
				[],
194
				[ 'P1' ]
195
			],
196
			[
197
				[ new PropertyNoValueSnak( $id1 ) ],
198
				[ new PropertyNoValueSnak( $id1 ) ],
199
			],
200
			[
201
				[
202
					new PropertyNoValueSnak( $id2 ),
203
					new PropertyNoValueSnak( $id1 ),
204
				],
205
				[
206
					new PropertyNoValueSnak( $id2 ),
207
					new PropertyNoValueSnak( $id1 ),
208
				],
209
			],
210
			[
211
				[
212
					new PropertyNoValueSnak( $id1 ),
213
					new PropertyNoValueSnak( $id2 ),
214
					new PropertyValueSnak( $id1, new StringValue( 'a' ) ),
215
				],
216
				[
217
					new PropertyNoValueSnak( $id1 ),
218
					new PropertyValueSnak( $id1, new StringValue( 'a' ) ),
219
					new PropertyNoValueSnak( $id2 ),
220
				],
221
			],
222
			'With additional order' => [
223
				[
224
					new PropertyNoValueSnak( $id3 ),
225
					new PropertyNoValueSnak( $id2 ),
226
					new PropertyValueSnak( $id1, new StringValue( 'a' ) ),
227
				],
228
				[
229
					new PropertyNoValueSnak( $id2 ),
230
					new PropertyNoValueSnak( $id3 ),
231
					new PropertyValueSnak( $id1, new StringValue( 'a' ) ),
232
				],
233
				[ 'P2' ]
234
			],
235
			[
236
				[
237
					new PropertyNoValueSnak( $id3 ),
238
					new PropertyNoValueSnak( $id2 ),
239
					new PropertyNoValueSnak( $id2 ),
240
					new PropertyValueSnak( $id1, new StringValue( 'a' ) ),
241
					new PropertyNoValueSnak( $id1 ),
242
				],
243
				[
244
					new PropertyValueSnak( $id1, new StringValue( 'a' ) ),
245
					new PropertyNoValueSnak( $id1 ),
246
					new PropertyNoValueSnak( $id3 ),
247
					new PropertyNoValueSnak( $id2 ),
248
					new PropertyNoValueSnak( $id2 ),
249
				],
250
				[ 'P1' ]
251
			],
252
			'Multiple IDs in order' => [
253
				[
254
					new PropertyValueSnak( $id1, new StringValue( 'a' ) ),
255
					new PropertyValueSnak( $id2, new StringValue( 'b' ) ),
256
					new PropertyValueSnak( $id1, new StringValue( 'c' ) ),
257
					new PropertyValueSnak( $id3, new StringValue( 'd' ) ),
258
					new PropertyValueSnak( $id4, new StringValue( 'e' ) ),
259
					new PropertyValueSnak( $id2, new StringValue( 'f' ) ),
260
					new PropertyValueSnak( $id4, new StringValue( 'g' ) ),
261
				],
262
				[
263
					new PropertyValueSnak( $id2, new StringValue( 'b' ) ),
264
					new PropertyValueSnak( $id2, new StringValue( 'f' ) ),
265
					new PropertyValueSnak( $id3, new StringValue( 'd' ) ),
266
					new PropertyValueSnak( $id1, new StringValue( 'a' ) ),
267
					new PropertyValueSnak( $id1, new StringValue( 'c' ) ),
268
					new PropertyValueSnak( $id4, new StringValue( 'e' ) ),
269
					new PropertyValueSnak( $id4, new StringValue( 'g' ) ),
270
				],
271
				[ 'P2', 'P3', 'P1' ]
272
			],
273
		];
274
275
		$arguments = [];
276
277
		foreach ( $rawArguments as $key => $rawArgument ) {
278
			$arguments[$key] = [
279
				new SnakList( $rawArgument[0] ),
280
				new SnakList( $rawArgument[1] ),
281
				array_key_exists( 2, $rawArgument ) ? $rawArgument[2] : []
282
			];
283
		}
284
285
		return $arguments;
286
	}
287
288
	/**
289
	 * @dataProvider orderByPropertyProvider
290
	 */
291
	public function testOrderByProperty( SnakList $snakList, SnakList $expected, array $order = [] ) {
292
		$initialSnakList = new SnakList( array_values( iterator_to_array( $snakList ) ) );
293
294
		$snakList->orderByProperty( $order );
295
296
		// Instantiate new SnakList resetting the snaks' array keys. This allows comparing the
297
		// reordered SnakList to the expected SnakList.
298
		$orderedSnakList = new SnakList( array_values( iterator_to_array( $snakList ) ) );
299
300
		$this->assertEquals( $expected, $orderedSnakList );
301
302
		if ( $orderedSnakList->equals( $initialSnakList ) ) {
303
			$this->assertSame( $initialSnakList->getHash(), $snakList->getHash() );
304
		} else {
305
			$this->assertNotSame( $initialSnakList->getHash(), $snakList->getHash() );
306
		}
307
308
		/** @var Snak $snak */
309
		foreach ( $snakList as $snak ) {
310
			$hash = $snak->getHash();
311
			$this->assertSame(
312
				$hash,
313
				$snakList->getSnak( $hash )->getHash(),
314
				'Reordering must not mess up the lists internal state'
315
			);
316
		}
317
	}
318
319
	public function testComparableInterface() {
320
		$this->assertInstanceOf( Comparable::class, new SnakList() );
321
	}
322
323
	/**
324
	 * @dataProvider equalsProvider
325
	 */
326
	public function testEquals( SnakList $list1, SnakList $list2, $expected ) {
327
		$this->assertSame( $expected, $list1->equals( $list2 ) );
328
	}
329
330
	public function equalsProvider() {
331
		$empty = new SnakList();
332
		$oneSnak = new SnakList( [ new PropertyNoValueSnak( 1 ) ] );
333
334
		return [
335
			'empty object is equal to itself' => [
336
				$empty,
337
				$empty,
338
				true
339
			],
340
			'non-empty object is equal to itself' => [
341
				$oneSnak,
342
				$oneSnak,
343
				true
344
			],
345
			'different empty objects are equal' => [
346
				$empty,
347
				new SnakList(),
348
				true
349
			],
350
			'different objects with same content are equal' => [
351
				$oneSnak,
352
				new SnakList( [ new PropertyNoValueSnak( 1 ) ] ),
353
				true
354
			],
355
			'different objects with different content are not equal' => [
356
				$oneSnak,
357
				new SnakList( [ new PropertyNoValueSnak( 2 ) ] ),
358
				false
359
			],
360
		];
361
	}
362
363
	public function testHashableInterface() {
364
		$this->assertInstanceOf( Hashable::class, new SnakList() );
365
	}
366
367
	public function testGetHash() {
368
		$snakList = new SnakList( [ new PropertyNoValueSnak( 1 ) ] );
369
		$hash = $snakList->getHash();
370
371
		$this->assertInternalType( 'string', $hash, 'must be a string' );
372
		$this->assertNotSame( '', $hash, 'must not be empty' );
373
		$this->assertSame( $hash, $snakList->getHash(), 'second call must return the same hash' );
374
375
		$otherList = new SnakList( [ new PropertyNoValueSnak( 2 ) ] );
376
		$this->assertNotSame( $hash, $otherList->getHash() );
377
	}
378
379
	/**
380
	 * This integration test (relies on SnakObject::getHash) is supposed to break whenever the hash
381
	 * calculation changes.
382
	 */
383
	public function testHashStability() {
384
		$snakList = new SnakList();
385
		$this->assertSame( 'da39a3ee5e6b4b0d3255bfef95601890afd80709', $snakList->getHash() );
386
387
		$snakList = new SnakList( [ new PropertyNoValueSnak( 1 ) ] );
388
		$this->assertSame( '4327ac5109aaf437ccce05580c563a5857d96c82', $snakList->getHash() );
389
	}
390
391
	/**
392
	 * @dataProvider provideEqualSnakLists
393
	 */
394
	public function testGivenEqualSnakLists_getHashIsTheSame( SnakList $self, SnakList $other ) {
395
		$this->assertSame( $self->getHash(), $other->getHash() );
396
	}
397
398
	public function provideEqualSnakLists() {
399
		$empty = new SnakList();
400
		$oneSnak = new SnakList( [ new PropertyNoValueSnak( 1 ) ] );
401
402
		return [
403
			'same empty object' => [ $empty, $empty ],
404
			'same non-empty object' => [ $oneSnak, $oneSnak ],
405
			'equal empty objects' => [ $empty, new SnakList() ],
406
			'equal non-empty objects' => [ $oneSnak, new SnakList( [ new PropertyNoValueSnak( 1 ) ] ) ],
407
		];
408
	}
409
410
}
411