Passed
Push — master ( 989ec2...f800f0 )
by Leszek
44s
created

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