Test Setup Failed
Push — master ( 94a38d...c05df4 )
by
unknown
01:04 queued 11s
created

SnakListTest::testGetHash()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

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