Test Failed
Push — moveSnaksToBottom-optimize2 ( 91f09b )
by no
02:16
created

testGivenAssociativeArray_constructorPreservesArrayKeys()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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