Passed
Push — master ( 71d19a...11b062 )
by adam
42s
created

testWhenThereAreChangeOperations_getChangesReturnsThem()   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
declare( strict_types = 1 );
4
5
namespace Diff\Tests\DiffOp\Diff;
6
7
use Closure;
8
use Diff\DiffOp\Diff\Diff;
9
use Diff\DiffOp\DiffOp;
10
use Diff\DiffOp\DiffOpAdd;
11
use Diff\DiffOp\DiffOpChange;
12
use Diff\DiffOp\DiffOpRemove;
13
use Diff\Tests\DiffTestCase;
14
use stdClass;
15
16
/**
17
 * @covers \Diff\DiffOp\Diff\Diff
18
 *
19
 * @group Diff
20
 * @group DiffOp
21
 *
22
 * @license BSD-3-Clause
23
 * @author Jeroen De Dauw < [email protected] >
24
 * @author Thiemo Kreuz
25
 */
26
class DiffTest extends DiffTestCase {
27
28
	public function elementInstancesProvider() {
29
		return array(
30
			array( array(
31
			) ),
32
			array( array(
33
				new DiffOpAdd( 'ohi' )
34
			) ),
35
			array( array(
36
				new DiffOpRemove( 'ohi' )
37
			) ),
38
			array( array(
39
				new DiffOpAdd( 'ohi' ),
40
				new DiffOpRemove( 'there' )
41
			) ),
42
			array( array(
43
			) ),
44
			array( array(
45
				new DiffOpAdd( 'ohi' ),
46
				new DiffOpRemove( 'there' ),
47
				new DiffOpChange( 'ohi', 'there' )
48
			) ),
49
			array( array(
50
				'1' => new DiffOpAdd( 'ohi' ),
51
				'33' => new DiffOpRemove( 'there' ),
52
				'7' => new DiffOpChange( 'ohi', 'there' )
53
			) ),
54
		);
55
	}
56
57
	/**
58
	 * @dataProvider elementInstancesProvider
59
	 * @param DiffOp[] $operations
60
	 */
61
	public function testGetAdditions( array $operations ) {
62
		$diff = new Diff( $operations, true );
63
64
		$additions = array();
65
66
		foreach ( $operations as $operation ) {
67
			if ( $operation->getType() == 'add' ) {
68
				$additions[] = $operation;
69
			}
70
		}
71
72
		$this->assertArrayEquals( $additions, $diff->getAdditions() );
73
	}
74
75
	/**
76
	 * @dataProvider elementInstancesProvider
77
	 * @param DiffOp[] $operations
78
	 */
79
	public function testGetRemovals( array $operations ) {
80
		$diff = new Diff( $operations, true );
81
82
		$removals = array();
83
84
		foreach ( $operations as $operation ) {
85
			if ( $operation->getType() == 'remove' ) {
86
				$removals[] = $operation;
87
			}
88
		}
89
90
		$this->assertArrayEquals( $removals, $diff->getRemovals() );
91
	}
92
93
	public function testGetType() {
94
		$diff = new Diff();
95
		$this->assertInternalType( 'string', $diff->getType() );
96
	}
97
98
	public function testPreSetElement() {
99
		$this->expectException( 'Exception' );
100
101
		$diff = new Diff( array(), false );
102
		$diff[] = new DiffOpChange( 0, 1 );
103
	}
104
105
	/**
106
	 * @dataProvider elementInstancesProvider
107
	 * @param DiffOp[] $operations
108
	 */
109
	public function testAddOperations( array $operations ) {
110
		$diff = new Diff();
111
112
		$diff->addOperations( $operations );
113
114
		$this->assertArrayEquals( $operations, $diff->getOperations() );
115
	}
116
117
	/**
118
	 * @dataProvider elementInstancesProvider
119
	 * @param DiffOp[] $operations
120
	 */
121
	public function testStuff( array $operations ) {
122
		$diff = new Diff( $operations );
123
124
		$this->assertInstanceOf( 'Diff\DiffOp\Diff\Diff', $diff );
125
		$this->assertInstanceOf( 'ArrayObject', $diff );
126
127
		$types = array();
128
129
		$this->assertContainsOnlyInstancesOf( 'Diff\DiffOp\DiffOp', $diff );
130
131
		/**
132
		 * @var DiffOp $operation
133
		 */
134
		foreach ( $diff as $operation ) {
135
			if ( !in_array( $operation->getType(), $types ) ) {
136
				$types[] = $operation->getType();
137
			}
138
		}
139
140
		$count = 0;
141
142
		foreach ( $types as $type ) {
143
			$count += count( $diff->getTypeOperations( $type ) );
144
		}
145
146
		$this->assertEquals( $count, $diff->count() );
147
	}
148
149
	public function instanceProvider() {
150
		foreach ( $this->elementInstancesProvider() as $args ) {
151
			yield [ new Diff( $args[0] ) ];
152
		}
153
	}
154
155
	/**
156
	 * @dataProvider instanceProvider
157
	 */
158
	public function testGetOperations( Diff $diff ) {
159
		$ops = $diff->getOperations();
160
161
		$this->assertInternalType( 'array', $ops );
162
		$this->assertContainsOnlyInstancesOf( 'Diff\DiffOp\DiffOp', $ops );
163
		$this->assertArrayEquals( $ops, $diff->getOperations() );
164
	}
165
166
	public function testRemoveEmptyOperations() {
167
		$diff = new Diff( array() );
168
169
		$diff['foo'] = new DiffOpAdd( 1 );
170
		$diff['bar'] = new Diff( array( new DiffOpAdd( 1 ) ), true );
171
		$diff['baz'] = new Diff( array( new DiffOpAdd( 1 ) ), false );
172
		$diff['bah'] = new Diff( array(), false );
173
		$diff['spam'] = new Diff( array(), true );
174
175
		$diff->removeEmptyOperations();
176
177
		$this->assertTrue( $diff->offsetExists( 'foo' ) );
178
		$this->assertTrue( $diff->offsetExists( 'bar' ) );
179
		$this->assertTrue( $diff->offsetExists( 'baz' ) );
180
		$this->assertFalse( $diff->offsetExists( 'bah' ) );
181
		$this->assertFalse( $diff->offsetExists( 'spam' ) );
182
	}
183
184
	public function looksAssociativeProvider() {
185
		return array(
186
			array( new Diff(), false ),
187
			array( new Diff( array(), false ), false ),
188
			array( new Diff( array(), true ), true ),
189
			array( new Diff( array( new DiffOpAdd( '' ) ) ), false ),
190
			array( new Diff( array( new DiffOpRemove( '' ) ) ), false ),
191
			array( new Diff( array( new DiffOpRemove( '' ), new DiffOpAdd( '' ) ) ), false ),
192
			array( new Diff( array( new DiffOpRemove( '' ) ), true ), true ),
193
			array( new Diff( array( 'onoez' => new DiffOpChange( '', 'spam' ) ) ), true ),
194
			array( new Diff( array( new Diff() ) ), true ),
195
		);
196
	}
197
198
	/**
199
	 * @dataProvider looksAssociativeProvider
200
	 */
201
	public function testLooksAssociative( Diff $diff, $looksAssoc ) {
202
		$this->assertEquals( $looksAssoc, $diff->looksAssociative() );
203
204
		if ( !$diff->looksAssociative() ) {
205
			$this->assertFalse( $diff->hasAssociativeOperations() );
206
		}
207
	}
208
209
	public function isAssociativeProvider() {
210
		return array(
211
			array( new Diff(), null ),
212
			array( new Diff( array(), false ), false ),
213
			array( new Diff( array(), true ), true ),
214
			array( new Diff( array( new DiffOpAdd( '' ) ) ), null ),
215
			array( new Diff( array( new DiffOpRemove( '' ) ), false ), false ),
216
			array( new Diff( array( new DiffOpRemove( '' ), new DiffOpAdd( '' ) ) ), null ),
217
			array( new Diff( array( new DiffOpRemove( '' ) ), true ), true ),
218
			array( new Diff( array( 'onoez' => new DiffOpChange( '', 'spam' ) ) ), null ),
219
			array( new Diff( array( new Diff() ) ), null ),
220
		);
221
	}
222
223
	/**
224
	 * @dataProvider isAssociativeProvider
225
	 */
226
	public function testIsAssociative( Diff $diff, $isAssoc ) {
227
		$this->assertEquals( $isAssoc, $diff->isAssociative() );
228
	}
229
230
	public function hasAssociativeOperationsProvider() {
231
		return array(
232
			array( new Diff(), false ),
233
			array( new Diff( array(), false ), false ),
234
			array( new Diff( array(), true ), false ),
235
			array( new Diff( array( new DiffOpAdd( '' ) ) ), false ),
236
			array( new Diff( array( new DiffOpRemove( '' ) ), false ), false ),
237
			array( new Diff( array( new DiffOpRemove( '' ), new DiffOpAdd( '' ) ), true ), false ),
238
			array( new Diff( array( new DiffOpRemove( '' ) ), true ), false ),
239
			array( new Diff( array( 'onoez' => new DiffOpChange( '', 'spam' ) ) ), true ),
240
			array( new Diff( array( new Diff() ) ), true ),
241
		);
242
	}
243
244
	/**
245
	 * @dataProvider hasAssociativeOperationsProvider
246
	 */
247
	public function testHasAssociativeOperations( Diff $diff, $hasAssocOps ) {
248
		$this->assertEquals( $hasAssocOps, $diff->hasAssociativeOperations() );
249
	}
250
251
	/**
252
	 * @dataProvider elementInstancesProvider
253
	 *
254
	 * @since 0.6
255
	 *
256
	 * @param DiffOp[] $elements
257
	 */
258
	public function testConstructor( array $elements ) {
259
		$arrayObject = new Diff( $elements );
260
261
		$this->assertEquals( count( $elements ), $arrayObject->count() );
262
	}
263
264
	/**
265
	 * @dataProvider elementInstancesProvider
266
	 *
267
	 * @since 0.6
268
	 *
269
	 * @param DiffOp[] $elements
270
	 */
271
	public function testIsEmpty( array $elements ) {
272
		$arrayObject = new Diff( $elements );
273
274
		$this->assertEquals( $elements === array(), $arrayObject->isEmpty() );
275
	}
276
277
	/**
278
	 * @dataProvider instanceProvider
279
	 *
280
	 * @since 0.6
281
	 *
282
	 * @param Diff $list
283
	 */
284
	public function testUnset( Diff $list ) {
285
		if ( $list->isEmpty() ) {
286
			$this->assertTrue( true ); // We cannot test unset if there are no elements
287
		} else {
288
			$offset = $list->getIterator()->key();
289
			$count = $list->count();
290
			$list->offsetUnset( $offset );
291
			$this->assertEquals( $count - 1, $list->count() );
292
		}
293
294
		if ( !$list->isEmpty() ) {
295
			$offset = $list->getIterator()->key();
296
			$count = $list->count();
297
			unset( $list[$offset] );
298
			$this->assertEquals( $count - 1, $list->count() );
299
		}
300
	}
301
302
	/**
303
	 * @dataProvider elementInstancesProvider
304
	 *
305
	 * @since 0.6
306
	 *
307
	 * @param DiffOp[] $elements
308
	 */
309
	public function testAppend( array $elements ) {
310
		$list = new Diff();
311
312
		$listSize = count( $elements );
313
314
		foreach ( $elements as $element ) {
315
			$list->append( $element );
316
		}
317
318
		$this->assertEquals( $listSize, $list->count() );
319
320
		$list = new Diff();
321
322
		foreach ( $elements as $element ) {
323
			$list[] = $element;
324
		}
325
326
		$this->assertEquals( $listSize, $list->count() );
327
328
		$this->checkTypeChecks( function ( Diff $list, $element ) {
329
			$list->append( $element );
330
		} );
331
	}
332
333
	/**
334
	 * @param Closure $function
335
	 */
336
	private function checkTypeChecks( Closure $function ) {
337
		$excption = null;
0 ignored issues
show
Unused Code introduced by jeroendedauw
$excption is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
338
		$list = new Diff();
339
340
		foreach ( array( 42, 'foo', array(), new stdClass(), 4.2 ) as $element ) {
341
			$this->assertInvalidArgument( $function, $list, $element );
342
		}
343
	}
344
345
	/**
346
	 * Asserts that an InvalidArgumentException gets thrown when calling the provided
347
	 * callable. Extra arguments specified to the method are also provided to the callable.
348
	 *
349
	 * @param Closure $function
350
	 */
351
	private function assertInvalidArgument( Closure $function ) {
352
		$this->expectException( 'InvalidArgumentException' );
353
354
		$arguments = func_get_args();
355
		array_shift( $arguments );
356
357
		call_user_func_array( $function, $arguments );
358
	}
359
360
	public function testGetAddedValues() {
361
		$diff = new Diff( array(
362
			new DiffOpAdd( 0 ),
363
			new DiffOpRemove( 1 ),
364
			new DiffOpAdd( 2 ),
365
			new DiffOpRemove( 3 ),
366
			new DiffOpAdd( 4 ),
367
			new DiffOpChange( 7, 5 ),
368
			new Diff( array( new DiffOpAdd( 9 ) ) ),
369
		) );
370
371
		$addedValues = $diff->getAddedValues();
372
373
		$this->assertInternalType( 'array', $addedValues );
374
375
		$this->assertArrayEquals( array( 0, 2, 4 ), $addedValues );
376
377
		$diff = new Diff();
378
		$this->assertArrayEquals( array(), $diff->getAddedValues() );
379
	}
380
381
	public function testGetRemovedValues() {
382
		$diff = new Diff( array(
383
			new DiffOpAdd( 0 ),
384
			new DiffOpRemove( 1 ),
385
			new DiffOpAdd( 2 ),
386
			new DiffOpRemove( 3 ),
387
			new DiffOpAdd( 4 ),
388
			new DiffOpChange( 6, 4 ),
389
			new Diff( array( new DiffOPRemove( 8 ) ) ),
390
		) );
391
392
		$removedValues = $diff->getRemovedValues();
393
394
		$this->assertInternalType( 'array', $removedValues );
395
396
		$this->assertArrayEquals( array( 1, 3 ), $removedValues );
397
398
		$diff = new Diff();
399
		$this->assertArrayEquals( array(), $diff->getRemovedValues() );
400
	}
401
402
	/**
403
	 * @dataProvider elementInstancesProvider
404
	 *
405
	 * @since 0.6
406
	 *
407
	 * @param DiffOp[] $elements
408
	 */
409
	public function testOffsetSet( array $elements ) {
410
		if ( $elements === array() ) {
411
			$this->assertTrue( true );
412
			return;
413
		}
414
415
		$list = new Diff();
416
417
		$element = reset( $elements );
418
		$list->offsetSet( 42, $element );
419
		$this->assertEquals( $element, $list->offsetGet( 42 ) );
420
421
		$list = new Diff();
422
423
		$element = reset( $elements );
424
		$list['oHai'] = $element;
425
		$this->assertEquals( $element, $list['oHai'] );
426
427
		$list = new Diff();
428
429
		$element = reset( $elements );
430
		$list->offsetSet( 9001, $element );
431
		$this->assertEquals( $element, $list[9001] );
432
433
		$list = new Diff();
434
435
		$element = reset( $elements );
436
		$list->offsetSet( null, $element );
437
		$this->assertEquals( $element, $list[0] );
438
439
		$list = new Diff();
440
		$offset = 0;
441
442
		foreach ( $elements as $element ) {
443
			$list->offsetSet( null, $element );
444
			$this->assertEquals( $element, $list[$offset++] );
445
		}
446
447
		$this->assertEquals( count( $elements ), $list->count() );
448
449
		$this->checkTypeChecks( function ( Diff $list, $element ) {
450
			$list->offsetSet( mt_rand(), $element );
451
		} );
452
	}
453
454
	/**
455
	 * @dataProvider instanceProvider
456
	 *
457
	 * @since 0.6
458
	 *
459
	 * @param Diff $list
460
	 */
461
	public function testSerialization( Diff $list ) {
462
		$serialization = serialize( $list );
463
		$copy = unserialize( $serialization );
464
465
		$this->assertSame( $serialization, serialize( $copy ) );
466
		$this->assertSame( $list->count(), $copy->count() );
467
468
		$list = $list->getArrayCopy();
469
		$copy = $copy->getArrayCopy();
470
471
		$this->assertArrayEquals( $list, $copy, true, true );
472
	}
473
474
	/**
475
	 * @dataProvider instanceProvider
476
	 *
477
	 * @since 0.6
478
	 *
479
	 * @param Diff $list
480
	 */
481
	public function testAddInvalidDiffOp( Diff $list ) {
482
		$invalidDiffOp = $this->createMock( 'Diff\DiffOp\DiffOp' );
483
484
		$invalidDiffOp->expects( $this->atLeastOnce() )
485
			->method( 'getType' )
486
			->will( $this->returnValue( '~=[,,_,,]:3' ) );
487
488
		$this->expectException( 'Exception' );
489
490
		$list->append( $invalidDiffOp );
491
	}
492
493
	/**
494
	 * @dataProvider invalidIsAssociativeProvider
495
	 */
496
	public function testConstructWithInvalidIsAssociative( $isAssociative ) {
497
		$this->expectException( 'InvalidArgumentException' );
498
		new Diff( array(), $isAssociative );
499
	}
500
501
	public function invalidIsAssociativeProvider() {
502
		return array(
503
			array( 1 ),
504
			array( '1' ),
505
			array( 'null' ),
506
			array( 0 ),
507
			array( array() ),
508
			array( 'foobar' ),
509
		);
510
	}
511
512
	/**
513
	 * @dataProvider invalidDiffOpsProvider
514
	 */
515
	public function testConstructorWithInvalidDiffOps( array $diffOps ) {
516
		$this->expectException( 'InvalidArgumentException' );
517
		new Diff( $diffOps );
518
	}
519
520
	public function invalidDiffOpsProvider() {
521
		return array(
522
			array( array(
523
				'foo',
524
			) ),
525
			array( array(
526
				null,
527
			) ),
528
			array( array(
529
				false,
530
				true,
531
				array(),
532
			) ),
533
			array( array(
534
				new DiffOpAdd( 42 ),
535
				'in your list',
536
				new DiffOpAdd( 9001 ),
537
			) )
538
		);
539
	}
540
541
	/**
542
	 * @dataProvider equalsProvider
543
	 */
544
	public function testEquals( Diff $diff, Diff $target ) {
545
		$this->assertTrue( $diff->equals( $target ) );
546
		$this->assertTrue( $target->equals( $diff ) );
547
	}
548
549
	public function equalsProvider() {
550
		$empty = new Diff();
551
552
		return array(
553
			// Identity
554
			array( $empty, $empty ),
555
556
			// Empty diffs
557
			array( $empty, new Diff() ),
558
			array( $empty, new Diff( array(), null ) ),
559
560
			// Simple diffs
561
			array( new Diff( array( new DiffOpAdd( 1 ) ) ), new Diff( array( new DiffOpAdd( 1 ) ) ) ),
562
			array( new Diff( array( new DiffOpAdd( 1 ) ) ), new Diff( array( new DiffOpAdd( '1' ) ) ) ),
563
		);
564
	}
565
566
	/**
567
	 * @dataProvider notEqualsProvider
568
	 */
569
	public function testNotEquals( Diff $diff, $target ) {
570
		$this->assertFalse( $diff->equals( $target ) );
571
	}
572
573
	public function notEqualsProvider() {
574
		return array(
575
			// Not an instance or subclass of Diff
576
			array( new Diff(), null ),
577
			array( new Diff(), new DiffOpAdd( 1 ) ),
578
579
			// Empty diffs
580
			array( new Diff(), new Diff( array(), false ) ),
581
			array( new Diff(), new Diff( array(), true ) ),
582
583
			// Simple diffs
584
			array( new Diff(), new Diff( array( new DiffOpAdd( 1 ) ) ) ),
585
			array( new Diff( array( new DiffOpAdd( 1 ) ) ), new Diff( array( new DiffOpRemove( 1 ) ) ) ),
586
			array( new Diff( array( new DiffOpAdd( 1 ) ) ), new Diff( array( new DiffOpAdd( 2 ) ) ) ),
587
		);
588
	}
589
590
	public function testWhenThereAreNoChangeOperations_getChangesReturnsEmptyArray() {
591
		$this->assertSame(
592
			[],
593
			( new Diff( [
594
				new DiffOpAdd( 1 ),
595
				new DiffOpRemove( 2 )
596
			] ) )->getChanges()
597
		);
598
	}
599
600
	public function testWhenThereAreChangeOperations_getChangesReturnsThem() {
601
		$changeOperations = [
602
			new DiffOpChange( 1, 2 ),
603
			new DiffOpChange( 3, 4 )
604
		];
605
606
		$this->assertEquals(
607
			$changeOperations,
608
			( new Diff( $changeOperations ) )->getChanges()
609
		);
610
	}
611
612
}
613