SnakListTest   A
last analyzed

Complexity

Total Complexity 24

Size/Duplication

Total Lines 367
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 0
Metric Value
dl 0
loc 367
c 0
b 0
f 0
wmc 24
lcom 1
cbo 6
rs 10

16 Methods

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