Passed
Push — snakListArray ( c5b802 )
by no
02:24
created

SnakListTest::instanceProvider()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 20
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

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