1<?php 2/* 3 * This file is part of sebastian/diff. 4 * 5 * (c) Sebastian Bergmann <sebastian@phpunit.de> 6 * 7 * For the full copyright and license information, please view the LICENSE 8 * file that was distributed with this source code. 9 */ 10 11namespace SebastianBergmann\Diff; 12 13use SebastianBergmann\Diff\LCS\MemoryEfficientImplementation; 14use SebastianBergmann\Diff\LCS\TimeEfficientImplementation; 15use PHPUnit\Framework\TestCase; 16 17/** 18 * @covers SebastianBergmann\Diff\Differ 19 * 20 * @uses SebastianBergmann\Diff\LCS\MemoryEfficientImplementation 21 * @uses SebastianBergmann\Diff\LCS\TimeEfficientImplementation 22 * @uses SebastianBergmann\Diff\Chunk 23 * @uses SebastianBergmann\Diff\Diff 24 * @uses SebastianBergmann\Diff\Line 25 * @uses SebastianBergmann\Diff\Parser 26 */ 27class DifferTest extends TestCase 28{ 29 const REMOVED = 2; 30 const ADDED = 1; 31 const OLD = 0; 32 33 /** 34 * @var Differ 35 */ 36 private $differ; 37 38 protected function setUp() 39 { 40 $this->differ = new Differ; 41 } 42 43 /** 44 * @param array $expected 45 * @param string|array $from 46 * @param string|array $to 47 * @dataProvider arrayProvider 48 */ 49 public function testArrayRepresentationOfDiffCanBeRenderedUsingTimeEfficientLcsImplementation(array $expected, $from, $to) 50 { 51 $this->assertEquals($expected, $this->differ->diffToArray($from, $to, new TimeEfficientImplementation)); 52 } 53 54 /** 55 * @param string $expected 56 * @param string $from 57 * @param string $to 58 * @dataProvider textProvider 59 */ 60 public function testTextRepresentationOfDiffCanBeRenderedUsingTimeEfficientLcsImplementation($expected, $from, $to) 61 { 62 $this->assertEquals($expected, $this->differ->diff($from, $to, new TimeEfficientImplementation)); 63 } 64 65 /** 66 * @param array $expected 67 * @param string|array $from 68 * @param string|array $to 69 * @dataProvider arrayProvider 70 */ 71 public function testArrayRepresentationOfDiffCanBeRenderedUsingMemoryEfficientLcsImplementation(array $expected, $from, $to) 72 { 73 $this->assertEquals($expected, $this->differ->diffToArray($from, $to, new MemoryEfficientImplementation)); 74 } 75 76 /** 77 * @param string $expected 78 * @param string $from 79 * @param string $to 80 * @dataProvider textProvider 81 */ 82 public function testTextRepresentationOfDiffCanBeRenderedUsingMemoryEfficientLcsImplementation($expected, $from, $to) 83 { 84 $this->assertEquals($expected, $this->differ->diff($from, $to, new MemoryEfficientImplementation)); 85 } 86 87 public function testCustomHeaderCanBeUsed() 88 { 89 $differ = new Differ('CUSTOM HEADER'); 90 91 $this->assertEquals( 92 "CUSTOM HEADER@@ @@\n-a\n+b\n", 93 $differ->diff('a', 'b') 94 ); 95 } 96 97 public function testTypesOtherThanArrayAndStringCanBePassed() 98 { 99 $this->assertEquals( 100 "--- Original\n+++ New\n@@ @@\n-1\n+2\n", 101 $this->differ->diff(1, 2) 102 ); 103 } 104 105 /** 106 * @param string $diff 107 * @param Diff[] $expected 108 * @dataProvider diffProvider 109 */ 110 public function testParser($diff, array $expected) 111 { 112 $parser = new Parser; 113 $result = $parser->parse($diff); 114 115 $this->assertEquals($expected, $result); 116 } 117 118 public function arrayProvider() 119 { 120 return array( 121 array( 122 array( 123 array('a', self::REMOVED), 124 array('b', self::ADDED) 125 ), 126 'a', 127 'b' 128 ), 129 array( 130 array( 131 array('ba', self::REMOVED), 132 array('bc', self::ADDED) 133 ), 134 'ba', 135 'bc' 136 ), 137 array( 138 array( 139 array('ab', self::REMOVED), 140 array('cb', self::ADDED) 141 ), 142 'ab', 143 'cb' 144 ), 145 array( 146 array( 147 array('abc', self::REMOVED), 148 array('adc', self::ADDED) 149 ), 150 'abc', 151 'adc' 152 ), 153 array( 154 array( 155 array('ab', self::REMOVED), 156 array('abc', self::ADDED) 157 ), 158 'ab', 159 'abc' 160 ), 161 array( 162 array( 163 array('bc', self::REMOVED), 164 array('abc', self::ADDED) 165 ), 166 'bc', 167 'abc' 168 ), 169 array( 170 array( 171 array('abc', self::REMOVED), 172 array('abbc', self::ADDED) 173 ), 174 'abc', 175 'abbc' 176 ), 177 array( 178 array( 179 array('abcdde', self::REMOVED), 180 array('abcde', self::ADDED) 181 ), 182 'abcdde', 183 'abcde' 184 ), 185 'same start' => array( 186 array( 187 array(17, self::OLD), 188 array('b', self::REMOVED), 189 array('d', self::ADDED), 190 ), 191 array(30 => 17, 'a' => 'b'), 192 array(30 => 17, 'c' => 'd'), 193 ), 194 'same end' => array( 195 array( 196 array(1, self::REMOVED), 197 array(2, self::ADDED), 198 array('b', self::OLD), 199 ), 200 array(1 => 1, 'a' => 'b'), 201 array(1 => 2, 'a' => 'b'), 202 ), 203 'same start (2), same end (1)' => array( 204 array( 205 array(17, self::OLD), 206 array(2, self::OLD), 207 array(4, self::REMOVED), 208 array('a', self::ADDED), 209 array(5, self::ADDED), 210 array('x', self::OLD), 211 ), 212 array(30 => 17, 1 => 2, 2 => 4, 'z' => 'x'), 213 array(30 => 17, 1 => 2, 3 => 'a', 2 => 5, 'z' => 'x'), 214 ), 215 'same' => array( 216 array( 217 array('x', self::OLD), 218 ), 219 array('z' => 'x'), 220 array('z' => 'x'), 221 ), 222 'diff' => array( 223 array( 224 array('y', self::REMOVED), 225 array('x', self::ADDED), 226 ), 227 array('x' => 'y'), 228 array('z' => 'x'), 229 ), 230 'diff 2' => array( 231 array( 232 array('y', self::REMOVED), 233 array('b', self::REMOVED), 234 array('x', self::ADDED), 235 array('d', self::ADDED), 236 ), 237 array('x' => 'y', 'a' => 'b'), 238 array('z' => 'x', 'c' => 'd'), 239 ), 240 'test line diff detection' => array( 241 array( 242 array( 243 '#Warning: Strings contain different line endings!', 244 self::OLD, 245 ), 246 array( 247 '<?php', 248 self::OLD, 249 ), 250 array( 251 '', 252 self::OLD, 253 ), 254 ), 255 "<?php\r\n", 256 "<?php\n" 257 ) 258 ); 259 } 260 261 public function textProvider() 262 { 263 return array( 264 array( 265 "--- Original\n+++ New\n@@ @@\n-a\n+b\n", 266 'a', 267 'b' 268 ), 269 array( 270 "--- Original\n+++ New\n@@ @@\n-ba\n+bc\n", 271 'ba', 272 'bc' 273 ), 274 array( 275 "--- Original\n+++ New\n@@ @@\n-ab\n+cb\n", 276 'ab', 277 'cb' 278 ), 279 array( 280 "--- Original\n+++ New\n@@ @@\n-abc\n+adc\n", 281 'abc', 282 'adc' 283 ), 284 array( 285 "--- Original\n+++ New\n@@ @@\n-ab\n+abc\n", 286 'ab', 287 'abc' 288 ), 289 array( 290 "--- Original\n+++ New\n@@ @@\n-bc\n+abc\n", 291 'bc', 292 'abc' 293 ), 294 array( 295 "--- Original\n+++ New\n@@ @@\n-abc\n+abbc\n", 296 'abc', 297 'abbc' 298 ), 299 array( 300 "--- Original\n+++ New\n@@ @@\n-abcdde\n+abcde\n", 301 'abcdde', 302 'abcde' 303 ), 304 array( 305 "--- Original\n+++ New\n@@ @@\n-A\n+A1\n B\n", 306 "A\nB", 307 "A1\nB", 308 ), 309 array( 310 <<<EOF 311--- Original 312+++ New 313@@ @@ 314 a 315-b 316+p 317@@ @@ 318 i 319-j 320+w 321 k 322 323EOF 324 , 325 "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk", 326 "a\np\nc\nd\ne\nf\ng\nh\ni\nw\nk", 327 ), 328 array( 329 <<<EOF 330--- Original 331+++ New 332@@ @@ 333 a 334-b 335+p 336@@ @@ 337 i 338-j 339+w 340 k 341 342EOF 343 , 344 "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk", 345 "a\np\nc\nd\ne\nf\ng\nh\ni\nw\nk", 346 ), 347 ); 348 } 349 350 public function diffProvider() 351 { 352 $serialized_arr = <<<EOL 353a:1:{i:0;O:27:"SebastianBergmann\Diff\Diff":3:{s:33:"SebastianBergmann\Diff\Difffrom";s:7:"old.txt";s:31:"SebastianBergmann\Diff\Diffto";s:7:"new.txt";s:35:"SebastianBergmann\Diff\Diffchunks";a:3:{i:0;O:28:"SebastianBergmann\Diff\Chunk":5:{s:35:"SebastianBergmann\Diff\Chunkstart";i:1;s:40:"SebastianBergmann\Diff\ChunkstartRange";i:3;s:33:"SebastianBergmann\Diff\Chunkend";i:1;s:38:"SebastianBergmann\Diff\ChunkendRange";i:4;s:35:"SebastianBergmann\Diff\Chunklines";a:4:{i:0;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:1;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"2222111";}i:1;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"1111111";}i:2;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"1111111";}i:3;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"1111111";}}}i:1;O:28:"SebastianBergmann\Diff\Chunk":5:{s:35:"SebastianBergmann\Diff\Chunkstart";i:5;s:40:"SebastianBergmann\Diff\ChunkstartRange";i:10;s:33:"SebastianBergmann\Diff\Chunkend";i:6;s:38:"SebastianBergmann\Diff\ChunkendRange";i:8;s:35:"SebastianBergmann\Diff\Chunklines";a:11:{i:0;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"1111111";}i:1;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"1111111";}i:2;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"1111111";}i:3;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:8:"+1121211";}i:4;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"1111111";}i:5;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:8:"-1111111";}i:6;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:8:"-1111111";}i:7;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:8:"-2222222";}i:8;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"2222222";}i:9;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"2222222";}i:10;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"2222222";}}}i:2;O:28:"SebastianBergmann\Diff\Chunk":5:{s:35:"SebastianBergmann\Diff\Chunkstart";i:17;s:40:"SebastianBergmann\Diff\ChunkstartRange";i:5;s:33:"SebastianBergmann\Diff\Chunkend";i:16;s:38:"SebastianBergmann\Diff\ChunkendRange";i:6;s:35:"SebastianBergmann\Diff\Chunklines";a:6:{i:0;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"2222222";}i:1;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"2222222";}i:2;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"2222222";}i:3;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:8:"+2122212";}i:4;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"2222222";}i:5;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"2222222";}}}}}} 354EOL; 355 356 return array( 357 array( 358 "--- old.txt 2014-11-04 08:51:02.661868729 +0300\n+++ new.txt 2014-11-04 08:51:02.665868730 +0300\n@@ -1,3 +1,4 @@\n+2222111\n 1111111\n 1111111\n 1111111\n@@ -5,10 +6,8 @@\n 1111111\n 1111111\n 1111111\n +1121211\n 1111111\n -1111111\n -1111111\n -2222222\n 2222222\n 2222222\n 2222222\n@@ -17,5 +16,6 @@\n 2222222\n 2222222\n 2222222\n +2122212\n 2222222\n 2222222\n", 359 \unserialize($serialized_arr) 360 ) 361 ); 362 } 363 364 /** 365 * @param string $expected 366 * @param string $from 367 * @param string $to 368 * @dataProvider textForNoNonDiffLinesProvider 369 */ 370 public function testDiffDoNotShowNonDiffLines($expected, $from, $to) 371 { 372 $differ = new Differ('', false); 373 $this->assertSame($expected, $differ->diff($from, $to)); 374 } 375 376 public function textForNoNonDiffLinesProvider() 377 { 378 return array( 379 array( 380 '', 'a', 'a' 381 ), 382 array( 383 "-A\n+C\n", 384 "A\n\n\nB", 385 "C\n\n\nB", 386 ), 387 ); 388 } 389 390 /** 391 * @requires PHPUnit 5.7 392 */ 393 public function testDiffToArrayInvalidFromType() 394 { 395 $differ = new Differ; 396 397 $this->expectException('\InvalidArgumentException'); 398 $this->expectExceptionMessageRegExp('#^"from" must be an array or string\.$#'); 399 400 $differ->diffToArray(null, ''); 401 } 402 403 /** 404 * @requires PHPUnit 5.7 405 */ 406 public function testDiffInvalidToType() 407 { 408 $differ = new Differ; 409 410 $this->expectException('\InvalidArgumentException'); 411 $this->expectExceptionMessageRegExp('#^"to" must be an array or string\.$#'); 412 413 $differ->diffToArray('', new \stdClass); 414 } 415} 416