DifferTest.php 12 KB

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