Completed
Push — master ( d4745e...9b9256 )
by
unknown
27s
created

tests/phpunit/DiffOp/Diff/DiffTest.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Diff\Tests\DiffOp\Diff;
4
5
use Closure;
6
use Diff\DiffOp\Diff\Diff;
7
use Diff\DiffOp\Diff\ListDiff;
8
use Diff\DiffOp\Diff\MapDiff;
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
 * @licence GNU GPL v2+
23
 * @author Jeroen De Dauw < [email protected] >
24
 * @author Thiemo Mättig
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->setExpectedException( '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
		$instances = array();
151
152
		foreach ( $this->elementInstancesProvider() as $args ) {
153
			$diffOps = $args[0];
154
			$instances[] = array( new Diff( $diffOps ) );
155
		}
156
157
		return $instances;
158
	}
159
160
	/**
161
	 * @dataProvider instanceProvider
162
	 */
163
	public function testGetOperations( Diff $diff ) {
164
		$ops = $diff->getOperations();
165
166
		$this->assertInternalType( 'array', $ops );
167
		$this->assertContainsOnlyInstancesOf( 'Diff\DiffOp\DiffOp', $ops );
168
		$this->assertArrayEquals( $ops, $diff->getOperations() );
169
	}
170
171
	public function testRemoveEmptyOperations() {
172
		$diff = new Diff( array() );
173
174
		$diff['foo'] = new DiffOpAdd( 1 );
175
		$diff['bar'] = new Diff( array( new DiffOpAdd( 1 ) ), true );
176
		$diff['baz'] = new Diff( array( new DiffOpAdd( 1 ) ), false );
177
		$diff['bah'] = new Diff( array(), false );
178
		$diff['spam'] = new Diff( array(), true );
179
180
		$diff->removeEmptyOperations();
181
182
		$this->assertTrue( $diff->offsetExists( 'foo' ) );
183
		$this->assertTrue( $diff->offsetExists( 'bar' ) );
184
		$this->assertTrue( $diff->offsetExists( 'baz' ) );
185
		$this->assertFalse( $diff->offsetExists( 'bah' ) );
186
		$this->assertFalse( $diff->offsetExists( 'spam' ) );
187
	}
188
189
	public function looksAssociativeProvider() {
190
		$argLists = array();
191
192
		$diff = new Diff();
193
194
		$argLists[] = array( $diff, false );
195
196
197
		$diff = new Diff( array(), false );
198
199
		$argLists[] = array( $diff, false );
200
201
202
		$diff = new Diff( array(), true );
203
204
		$argLists[] = array( $diff, true );
205
206
207
		$diff = new Diff( array( new DiffOpAdd( '' ) ) );
208
209
		$argLists[] = array( $diff, false );
210
211
212
		$diff = new Diff( array( new DiffOpRemove( '' ) ) );
213
214
		$argLists[] = array( $diff, false );
215
216
217
		$diff = new Diff( array( new DiffOpRemove( '' ), new DiffOpAdd( '' ) ) );
218
219
		$argLists[] = array( $diff, false );
220
221
222
		$diff = new Diff( array( new DiffOpRemove( '' ) ), true );
223
224
		$argLists[] = array( $diff, true );
225
226
227
		$diff = new Diff( array( 'onoez' => new DiffOpChange( '', 'spam' ) ) );
228
229
		$argLists[] = array( $diff, true );
230
231
232
		$diff = new Diff( array( new Diff() ) );
233
234
		$argLists[] = array( $diff, true );
235
236
		return $argLists;
237
	}
238
239
	/**
240
	 * @dataProvider looksAssociativeProvider
241
	 */
242
	public function testLooksAssociative( Diff $diff, $looksAssoc ) {
243
		$this->assertEquals( $looksAssoc, $diff->looksAssociative() );
244
245
		if ( !$diff->looksAssociative() ) {
246
			$this->assertFalse( $diff->hasAssociativeOperations() );
247
		}
248
	}
249
250
	public function isAssociativeProvider() {
251
		$argLists = array();
252
253
		$diff = new Diff();
254
255
		$argLists[] = array( $diff, null );
256
257
258
		$diff = new Diff( array(), false );
259
260
		$argLists[] = array( $diff, false );
261
262
263
		$diff = new Diff( array(), true );
264
265
		$argLists[] = array( $diff, true );
266
267
268
		$diff = new Diff( array( new DiffOpAdd( '' ) ) );
269
270
		$argLists[] = array( $diff, null );
271
272
273
		$diff = new Diff( array( new DiffOpRemove( '' ) ), false );
274
275
		$argLists[] = array( $diff, false );
276
277
278
		$diff = new Diff( array( new DiffOpRemove( '' ), new DiffOpAdd( '' ) ) );
279
280
		$argLists[] = array( $diff, null );
281
282
283
		$diff = new Diff( array( new DiffOpRemove( '' ) ), true );
284
285
		$argLists[] = array( $diff, true );
286
287
288
		$diff = new Diff( array( 'onoez' => new DiffOpChange( '', 'spam' ) ) );
289
290
		$argLists[] = array( $diff, null );
291
292
293
		$diff = new Diff( array( new Diff() ) );
294
295
		$argLists[] = array( $diff, null );
296
297
		return $argLists;
298
	}
299
300
	/**
301
	 * @dataProvider isAssociativeProvider
302
	 */
303
	public function testIsAssociative( Diff $diff, $isAssoc ) {
304
		$this->assertEquals( $isAssoc, $diff->isAssociative() );
305
	}
306
307
	public function hasAssociativeOperationsProvider() {
308
		$argLists = array();
309
310
		$diff = new Diff();
311
312
		$argLists[] = array( $diff, false );
313
314
315
		$diff = new Diff( array(), false );
316
317
		$argLists[] = array( $diff, false );
318
319
320
		$diff = new Diff( array(), true );
321
322
		$argLists[] = array( $diff, false );
323
324
325
		$diff = new Diff( array( new DiffOpAdd( '' ) ) );
326
327
		$argLists[] = array( $diff, false );
328
329
330
		$diff = new Diff( array( new DiffOpRemove( '' ) ), false );
331
332
		$argLists[] = array( $diff, false );
333
334
335
		$diff = new Diff( array( new DiffOpRemove( '' ), new DiffOpAdd( '' ) ), true );
336
337
		$argLists[] = array( $diff, false );
338
339
340
		$diff = new Diff( array( new DiffOpRemove( '' ) ), true );
341
342
		$argLists[] = array( $diff, false );
343
344
345
		$diff = new Diff( array( 'onoez' => new DiffOpChange( '', 'spam' ) ) );
346
347
		$argLists[] = array( $diff, true );
348
349
350
		$diff = new Diff( array( new Diff() ) );
351
352
		$argLists[] = array( $diff, true );
353
354
		return $argLists;
355
	}
356
357
	/**
358
	 * @dataProvider hasAssociativeOperationsProvider
359
	 */
360
	public function testHasAssociativeOperations( Diff $diff, $hasAssocOps ) {
361
		$this->assertEquals( $hasAssocOps, $diff->hasAssociativeOperations() );
362
	}
363
364
	public function testSerializationCompat() {
365
		// @codingStandardsIgnoreStart
366
		$v03serialization = 'C:12:"Diff\MapDiff":569:{a:4:{s:4:"data";a:4:{i:0;C:14:"Diff\DiffOpAdd":10:{s:3:"add";}i:1;C:17:"Diff\DiffOpRemove":10:{s:3:"rem";}i:2;C:17:"Diff\DiffOpChange":30:{a:2:{i:0;s:1:"b";i:1;s:1:"a";}}i:3;C:13:"Diff\ListDiff":170:{a:4:{s:4:"data";a:1:{i:0;C:17:"Diff\DiffOpRemove":10:{s:3:"rem";}}s:5:"index";i:0;s:12:"typePointers";a:2:{s:3:"add";a:0:{}s:6:"remove";a:1:{i:0;i:0;}}s:9:"parentKey";N;}}}s:5:"index";i:0;s:12:"typePointers";a:6:{s:3:"add";a:1:{i:0;i:0;}s:6:"remove";a:1:{i:0;i:1;}s:6:"change";a:1:{i:0;i:2;}s:4:"list";a:1:{i:0;i:3;}s:3:"map";a:0:{}s:4:"diff";a:0:{}}s:9:"parentKey";N;}}';
367
		// @codingStandardsIgnoreEnd
368
369
		/**
370
		 * @var Diff $diff
371
		 */
372
		$diff = unserialize( $v03serialization );
373
374
		$this->assertInstanceOf( 'Diff\Diff', $diff );
375
		$this->assertTrue( $diff->isAssociative() );
376
		$this->assertSame( 4, $diff->count() );
377
		$this->assertSame( 1, count( $diff->getAdditions() ) );
378
		$this->assertSame( 1, count( $diff->getRemovals() ) );
379
		$this->assertSame( 1, count( $diff->getChanges() ) );
380
	}
381
382
	/**
383
	 * @dataProvider elementInstancesProvider
384
	 *
385
	 * @since 0.6
386
	 *
387
	 * @param DiffOp[] $elements
388
	 */
389
	public function testConstructor( array $elements ) {
390
		$arrayObject = new Diff( $elements );
391
392
		$this->assertEquals( count( $elements ), $arrayObject->count() );
393
	}
394
395
	/**
396
	 * @dataProvider elementInstancesProvider
397
	 *
398
	 * @since 0.6
399
	 *
400
	 * @param DiffOp[] $elements
401
	 */
402
	public function testIsEmpty( array $elements ) {
403
		$arrayObject = new Diff( $elements );
404
405
		$this->assertEquals( $elements === array(), $arrayObject->isEmpty() );
406
	}
407
408
	/**
409
	 * @dataProvider instanceProvider
410
	 *
411
	 * @since 0.6
412
	 *
413
	 * @param Diff $list
414
	 */
415
	public function testUnset( Diff $list ) {
416
		if ( $list->isEmpty() ) {
417
			$this->assertTrue( true ); // We cannot test unset if there are no elements
418
		} else {
419
			$offset = $list->getIterator()->key();
420
			$count = $list->count();
421
			$list->offsetUnset( $offset );
422
			$this->assertEquals( $count - 1, $list->count() );
423
		}
424
425
		if ( !$list->isEmpty() ) {
426
			$offset = $list->getIterator()->key();
427
			$count = $list->count();
428
			unset( $list[$offset] );
429
			$this->assertEquals( $count - 1, $list->count() );
430
		}
431
	}
432
433
	/**
434
	 * @dataProvider elementInstancesProvider
435
	 *
436
	 * @since 0.6
437
	 *
438
	 * @param DiffOp[] $elements
439
	 */
440
	public function testAppend( array $elements ) {
441
		$list = new Diff();
442
443
		$listSize = count( $elements );
444
445
		foreach ( $elements as $element ) {
446
			$list->append( $element );
447
		}
448
449
		$this->assertEquals( $listSize, $list->count() );
450
451
		$list = new Diff();
452
453
		foreach ( $elements as $element ) {
454
			$list[] = $element;
455
		}
456
457
		$this->assertEquals( $listSize, $list->count() );
458
459
		$this->checkTypeChecks( function ( Diff $list, $element ) {
460
			$list->append( $element );
461
		} );
462
	}
463
464
	/**
465
	 * @param Closure $function
466
	 */
467
	private function checkTypeChecks( Closure $function ) {
468
		$excption = null;
0 ignored issues
show
$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...
469
		$list = new Diff();
470
471
472
		foreach ( array( 42, 'foo', array(), new stdClass(), 4.2 ) as $element ) {
473
			$this->assertInvalidArgument( $function, $list, $element );
474
		}
475
	}
476
477
	/**
478
	 * Asserts that an InvalidArgumentException gets thrown when calling the provided
479
	 * callable. Extra arguments specified to the method are also provided to the callable.
480
	 *
481
	 * @param Closure $function
482
	 */
483
	private function assertInvalidArgument( Closure $function ) {
484
		$this->setExpectedException( 'InvalidArgumentException' );
485
486
		$arguments = func_get_args();
487
		array_shift( $arguments );
488
489
		call_user_func_array( $function, $arguments );
490
	}
491
492
	public function testGetAddedValues() {
493
		$diff = new Diff( array(
494
			new DiffOpAdd( 0 ),
495
			new DiffOpRemove( 1 ),
496
			new DiffOpAdd( 2 ),
497
			new DiffOpRemove( 3 ),
498
			new DiffOpAdd( 4 ),
499
			new DiffOpChange( 7, 5 ),
500
			new Diff( array( new DiffOpAdd( 9 ) ) ),
501
		) );
502
503
		$addedValues = $diff->getAddedValues();
504
505
		$this->assertInternalType( 'array', $addedValues );
506
507
		$this->assertArrayEquals( array( 0, 2, 4 ), $addedValues );
508
509
		$diff = new Diff();
510
		$this->assertArrayEquals( array(), $diff->getAddedValues() );
511
	}
512
513
	public function testGetRemovedValues() {
514
		$diff = new Diff( array(
515
			new DiffOpAdd( 0 ),
516
			new DiffOpRemove( 1 ),
517
			new DiffOpAdd( 2 ),
518
			new DiffOpRemove( 3 ),
519
			new DiffOpAdd( 4 ),
520
			new DiffOpChange( 6, 4 ),
521
			new Diff( array( new DiffOPRemove( 8 ) ) ),
522
		) );
523
524
		$removedValues = $diff->getRemovedValues();
525
526
		$this->assertInternalType( 'array', $removedValues );
527
528
		$this->assertArrayEquals( array( 1, 3 ), $removedValues );
529
530
		$diff = new Diff();
531
		$this->assertArrayEquals( array(), $diff->getRemovedValues() );
532
	}
533
534
	/**
535
	 * @dataProvider elementInstancesProvider
536
	 *
537
	 * @since 0.6
538
	 *
539
	 * @param DiffOp[] $elements
540
	 */
541
	public function testOffsetSet( array $elements ) {
542
		if ( $elements === array() ) {
543
			$this->assertTrue( true );
544
			return;
545
		}
546
547
		$list = new Diff();
548
549
		$element = reset( $elements );
550
		$list->offsetSet( 42, $element );
551
		$this->assertEquals( $element, $list->offsetGet( 42 ) );
552
553
		$list = new Diff();
554
555
		$element = reset( $elements );
556
		$list['oHai'] = $element;
557
		$this->assertEquals( $element, $list['oHai'] );
558
559
		$list = new Diff();
560
561
		$element = reset( $elements );
562
		$list->offsetSet( 9001, $element );
563
		$this->assertEquals( $element, $list[9001] );
564
565
		$list = new Diff();
566
567
		$element = reset( $elements );
568
		$list->offsetSet( null, $element );
569
		$this->assertEquals( $element, $list[0] );
570
571
		$list = new Diff();
572
		$offset = 0;
573
574
		foreach ( $elements as $element ) {
575
			$list->offsetSet( null, $element );
576
			$this->assertEquals( $element, $list[$offset++] );
577
		}
578
579
		$this->assertEquals( count( $elements ), $list->count() );
580
581
		$this->checkTypeChecks( function ( Diff $list, $element ) {
582
			$list->offsetSet( mt_rand(), $element );
583
		} );
584
	}
585
586
	/**
587
	 * @dataProvider instanceProvider
588
	 *
589
	 * @since 0.6
590
	 *
591
	 * @param Diff $list
592
	 */
593
	public function testSerialization( Diff $list ) {
594
		$serialization = serialize( $list );
595
		$copy = unserialize( $serialization );
596
597
		$this->assertSame( $serialization, serialize( $copy ) );
598
		$this->assertSame( $list->count(), $copy->count() );
599
600
		$list = $list->getArrayCopy();
601
		$copy = $copy->getArrayCopy();
602
603
		$this->assertArrayEquals( $list, $copy, true, true );
604
	}
605
606
	/**
607
	 * @dataProvider instanceProvider
608
	 *
609
	 * @since 0.6
610
	 *
611
	 * @param Diff $list
612
	 */
613
	public function testAddInvalidDiffOp( Diff $list ) {
614
		$invalidDiffOp = $this->getMock( 'Diff\DiffOp\DiffOp' );
615
616
		$invalidDiffOp->expects( $this->atLeastOnce() )
617
			->method( 'getType' )
618
			->will( $this->returnValue( '~=[,,_,,]:3' ) );
619
620
		$this->setExpectedException( 'Exception' );
621
622
		$list->append( $invalidDiffOp );
623
	}
624
625
	/**
626
	 * @dataProvider invalidIsAssociativeProvider
627
	 */
628
	public function testConstructWithInvalidIsAssociative( $isAssociative ) {
629
		$this->setExpectedException( 'InvalidArgumentException' );
630
		new Diff( array(), $isAssociative );
631
	}
632
633
	public function invalidIsAssociativeProvider() {
634
		return array(
635
			array( 1 ),
636
			array( '1' ),
637
			array( 'null' ),
638
			array( 0 ),
639
			array( array() ),
640
			array( 'foobar' ),
641
		);
642
	}
643
644
	/**
645
	 * @dataProvider invalidDiffOpsProvider
646
	 */
647
	public function testConstructorWithInvalidDiffOps( array $diffOps ) {
648
		$this->setExpectedException( 'InvalidArgumentException' );
649
		new Diff( $diffOps );
650
	}
651
652
	public function invalidDiffOpsProvider() {
653
		return array(
654
			array( array(
655
				'foo',
656
			) ),
657
			array( array(
658
				null,
659
			) ),
660
			array( array(
661
				false,
662
				true,
663
				array(),
664
			) ),
665
			array( array(
666
				new DiffOpAdd( 42 ),
667
				'in your list',
668
				new DiffOpAdd( 9001 ),
669
			) )
670
		);
671
	}
672
673
	/**
674
	 * @dataProvider equalsProvider
675
	 */
676
	public function testEquals( Diff $diff, Diff $target ) {
677
		$this->assertTrue( $diff->equals( $target ) );
678
		$this->assertTrue( $target->equals( $diff ) );
679
	}
680
681
	public function equalsProvider() {
682
		$empty = new Diff();
683
684
		return array(
685
			// Identity
686
			array( $empty, $empty ),
687
688
			// Empty diffs
689
			array( $empty, new Diff() ),
690
			array( $empty, new Diff( array(), null ) ),
691
692
			// Simple diffs
693
			array( new Diff( array( new DiffOpAdd( 1 ) ) ), new Diff( array( new DiffOpAdd( 1 ) ) ) ),
694
			array( new Diff( array( new DiffOpAdd( 1 ) ) ), new Diff( array( new DiffOpAdd( '1' ) ) ) ),
695
696
			// Subclasses that are shortcuts and should be equal
697
			array( new Diff( array(), false ), new ListDiff() ),
698
			array( new Diff( array(), true ), new MapDiff() ),
699
		);
700
	}
701
702
	/**
703
	 * @dataProvider notEqualsProvider
704
	 */
705
	public function testNotEquals( Diff $diff, $target ) {
706
		$this->assertFalse( $diff->equals( $target ) );
707
	}
708
709
	public function notEqualsProvider() {
710
		return array(
711
			// Not an instance or subclass of Diff
712
			array( new Diff(), null ),
713
			array( new Diff(), new DiffOpAdd( 1 ) ),
714
715
			// Empty diffs
716
			array( new Diff(), new Diff( array(), false ) ),
717
			array( new Diff(), new Diff( array(), true ) ),
718
719
			// Simple diffs
720
			array( new Diff(), new Diff( array( new DiffOpAdd( 1 ) ) ) ),
721
			array( new Diff( array( new DiffOpAdd( 1 ) ) ), new Diff( array( new DiffOpRemove( 1 ) ) ) ),
722
			array( new Diff( array( new DiffOpAdd( 1 ) ) ), new Diff( array( new DiffOpAdd( 2 ) ) ) ),
723
		);
724
	}
725
726
}
727