From 45ef3e4c875d7de185d704aaa49cd2be15fde48a Mon Sep 17 00:00:00 2001 From: SpacePossum Date: Mon, 21 Aug 2017 16:42:22 +0200 Subject: [PATCH 01/25] Backport for PHP 5.6. --- .editorconfig | 7 + .gitattributes | 13 ++ .gitignore | 6 +- .travis.yml | 37 ++--- README.md | 127 +----------------- composer.json | 16 +-- phpunit.xml => phpunit.xml.dist | 0 src/Chunk.php | 14 +- src/Diff.php | 10 +- src/Differ.php | 18 +-- src/Exception/Exception.php | 2 +- src/Exception/InvalidArgumentException.php | 2 +- src/Line.php | 8 +- src/LongestCommonSubsequenceCalculator.php | 4 +- ...ientLongestCommonSubsequenceCalculator.php | 6 +- src/Output/AbstractChunkOutputBuilder.php | 4 +- src/Output/DiffOnlyOutputBuilder.php | 6 +- src/Output/DiffOutputBuilderInterface.php | 4 +- src/Output/UnifiedDiffOutputBuilder.php | 20 +-- src/Parser.php | 4 +- ...ientLongestCommonSubsequenceCalculator.php | 4 +- tests/ChunkTest.php | 2 +- tests/CommonChunksTestOutputBuilder.php | 30 +++++ tests/DiffTest.php | 2 +- tests/DifferTest.php | 55 ++++---- tests/DifferTestTest.php | 4 +- tests/LineTest.php | 2 +- tests/LongestCommonSubsequenceTest.php | 2 +- tests/MemoryEfficientImplementationTest.php | 2 +- tests/ParserTest.php | 2 +- tests/TimeEfficientImplementationTest.php | 2 +- 31 files changed, 173 insertions(+), 242 deletions(-) create mode 100644 .editorconfig create mode 100644 .gitattributes rename phpunit.xml => phpunit.xml.dist (100%) create mode 100644 tests/CommonChunksTestOutputBuilder.php diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..d79a8557 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,7 @@ +root = true +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..1b6cf0d0 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,13 @@ +tests/ export-ignore +.editorconfig export-ignore +.gitattributes export-ignore +.gitignore export-ignore +.php_cs export-ignore +.travis.yml export-ignore +build.xml export-ignore +phpunit.xml.dist export-ignore +* text=auto eol=lf +*.php text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent,tabwidth=4 diff=php +*.json text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent,tabwidth=4 +*.yml text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent,tabwidth=4 +*.md text whitespace=blank-at-eol,blank-at-eof diff --git a/.gitignore b/.gitignore index cd879d7b..5cf9a2cf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,3 @@ -/.idea /composer.lock -/vendor -/.php_cs.cache -/from.txt.orig \ No newline at end of file +/phpunit.xml +/vendor/ diff --git a/.travis.yml b/.travis.yml index 90cd7269..cc99f189 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,27 +1,28 @@ language: php -php: - - 7.0 - - 7.0snapshot - - 7.1 - - 7.1snapshot - - master +git: + depth: 1 sudo: false -before_install: - - composer self-update - - composer clear-cache +matrix: + fast_finish: true + include: + - php: 5.6 + env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" + - php: 7.0 + env: COMPOSER_FLAGS="--prefer-stable" + - php: 7.1 + env: COMPOSER_FLAGS="--prefer-stable" + - php: nightly + env: COMPOSER_FLAGS="--dev --ignore-platform-reqs" + allow_failures: + - php: nightly install: - - travis_retry composer update --no-interaction --no-ansi --no-progress --no-suggest --optimize-autoloader --prefer-stable + - travis_retry composer update $COMPOSER_FLAGS --no-interaction -v + - composer info -D | sort script: - - ./vendor/bin/phpunit --coverage-clover=coverage.xml - -after_success: - - bash <(curl -s https://codecov.io/bash) - -notifications: - email: false - + - phpenv config-rm xdebug.ini || return 0 + - ./vendor/bin/phpunit --verbose diff --git a/README.md b/README.md index 956038b4..1a2f48d4 100644 --- a/README.md +++ b/README.md @@ -1,126 +1,7 @@ -# sebastian/diff +# PHP-CS-Fixer/diff -Diff implementation for PHP, factored out of PHPUnit into a stand-alone component. +Fork of sebastian/diff -## Installation +This is version is for PHP CS Fixer only! -You can add this library as a local, per-project dependency to your project using [Composer](https://getcomposer.org/): - - composer require sebastian/diff - -If you only need this library during development, for instance to run your project's test suite, then you should add it as a development-time dependency: - - composer require --dev sebastian/diff - -### Usage - -The `Differ` class can be used to generate a textual representation of the difference between two strings: - -```php -use SebastianBergmann\Diff\Differ; - -$differ = new Differ; -print $differ->diff('foo', 'bar'); -``` - -The code above yields the output below: - - --- Original - +++ New - @@ @@ - -foo - +bar - -The `Parser` class can be used to parse a unified diff into an object graph: - -```php -use SebastianBergmann\Diff\Parser; -use SebastianBergmann\Git; - -$git = new Git('/usr/local/src/money'); - -$diff = $git->getDiff( - '948a1a07768d8edd10dcefa8315c1cbeffb31833', - 'c07a373d2399f3e686234c4f7f088d635eb9641b' -); - -$parser = new Parser; - -print_r($parser->parse($diff)); -``` - -The code above yields the output below: - - Array - ( - [0] => SebastianBergmann\Diff\Diff Object - ( - [from:SebastianBergmann\Diff\Diff:private] => a/tests/MoneyTest.php - [to:SebastianBergmann\Diff\Diff:private] => b/tests/MoneyTest.php - [chunks:SebastianBergmann\Diff\Diff:private] => Array - ( - [0] => SebastianBergmann\Diff\Chunk Object - ( - [start:SebastianBergmann\Diff\Chunk:private] => 87 - [startRange:SebastianBergmann\Diff\Chunk:private] => 7 - [end:SebastianBergmann\Diff\Chunk:private] => 87 - [endRange:SebastianBergmann\Diff\Chunk:private] => 7 - [lines:SebastianBergmann\Diff\Chunk:private] => Array - ( - [0] => SebastianBergmann\Diff\Line Object - ( - [type:SebastianBergmann\Diff\Line:private] => 3 - [content:SebastianBergmann\Diff\Line:private] => * @covers SebastianBergmann\Money\Money::add - ) - - [1] => SebastianBergmann\Diff\Line Object - ( - [type:SebastianBergmann\Diff\Line:private] => 3 - [content:SebastianBergmann\Diff\Line:private] => * @covers SebastianBergmann\Money\Money::newMoney - ) - - [2] => SebastianBergmann\Diff\Line Object - ( - [type:SebastianBergmann\Diff\Line:private] => 3 - [content:SebastianBergmann\Diff\Line:private] => */ - ) - - [3] => SebastianBergmann\Diff\Line Object - ( - [type:SebastianBergmann\Diff\Line:private] => 2 - [content:SebastianBergmann\Diff\Line:private] => public function testAnotherMoneyWithSameCurrencyObjectCanBeAdded() - ) - - [4] => SebastianBergmann\Diff\Line Object - ( - [type:SebastianBergmann\Diff\Line:private] => 1 - [content:SebastianBergmann\Diff\Line:private] => public function testAnotherMoneyObjectWithSameCurrencyCanBeAdded() - ) - - [5] => SebastianBergmann\Diff\Line Object - ( - [type:SebastianBergmann\Diff\Line:private] => 3 - [content:SebastianBergmann\Diff\Line:private] => { - ) - - [6] => SebastianBergmann\Diff\Line Object - ( - [type:SebastianBergmann\Diff\Line:private] => 3 - [content:SebastianBergmann\Diff\Line:private] => $a = new Money(1, new Currency('EUR')); - ) - - [7] => SebastianBergmann\Diff\Line Object - ( - [type:SebastianBergmann\Diff\Line:private] => 3 - [content:SebastianBergmann\Diff\Line:private] => $b = new Money(2, new Currency('EUR')); - ) - - ) - - ) - - ) - - ) - - ) +Do not use it! diff --git a/composer.json b/composer.json index 5aaa7161..6e01327f 100644 --- a/composer.json +++ b/composer.json @@ -1,8 +1,8 @@ { - "name": "sebastian/diff", - "description": "Diff implementation", + "name": "PHP-CS-Fixer/diff", + "description": "sebastian/diff v2 backport support for PHP5.6", "keywords": ["diff"], - "homepage": "https://github.com/sebastianbergmann/diff", + "homepage": "https://github.com/PHP-CS-Fixer", "license": "BSD-3-Clause", "authors": [ { @@ -15,19 +15,17 @@ } ], "require": { - "php": "^7.0" + "php": "^5.6 || >=7.0 <7.2" }, "require-dev": { - "phpunit/phpunit": "^6.2" + "phpunit/phpunit": "^4.8.35 || ^5.4.3" }, "autoload": { "classmap": [ "src/" ] }, - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } + "autoload-dev": { + "psr-4": { "SebastianBergmann\\Diff\\Tests\\": "tests/" } } } diff --git a/phpunit.xml b/phpunit.xml.dist similarity index 100% rename from phpunit.xml rename to phpunit.xml.dist diff --git a/src/Chunk.php b/src/Chunk.php index 53339fce..2db5e7cd 100644 --- a/src/Chunk.php +++ b/src/Chunk.php @@ -1,4 +1,4 @@ -start = $start; $this->startRange = $startRange; @@ -46,27 +46,27 @@ public function __construct(int $start = 0, int $startRange = 1, int $end = 0, i $this->lines = $lines; } - public function getStart(): int + public function getStart() { return $this->start; } - public function getStartRange(): int + public function getStartRange() { return $this->startRange; } - public function getEnd(): int + public function getEnd() { return $this->end; } - public function getEndRange(): int + public function getEndRange() { return $this->endRange; } - public function getLines(): array + public function getLines() { return $this->lines; } diff --git a/src/Diff.php b/src/Diff.php index b94fd6f6..74d7c498 100644 --- a/src/Diff.php +++ b/src/Diff.php @@ -1,4 +1,4 @@ -from = $from; $this->to = $to; $this->chunks = $chunks; } - public function getFrom(): string + public function getFrom() { return $this->from; } - public function getTo(): string + public function getTo() { return $this->to; } @@ -52,7 +52,7 @@ public function getTo(): string /** * @return Chunk[] */ - public function getChunks(): array + public function getChunks() { return $this->chunks; } diff --git a/src/Differ.php b/src/Differ.php index 6e75b96e..a6eba763 100644 --- a/src/Differ.php +++ b/src/Differ.php @@ -1,4 +1,4 @@ -validateDiffInput($from); $to = $this->validateDiffInput($to); @@ -74,7 +74,7 @@ public function diff($from, $to, LongestCommonSubsequenceCalculator $lcs = null) * * @return string */ - private function validateDiffInput($input): string + private function validateDiffInput($input) { if (!\is_array($input) && !\is_string($input)) { return (string) $input; @@ -100,7 +100,7 @@ private function validateDiffInput($input): string * * @return array */ - public function diffToArray($from, $to, LongestCommonSubsequenceCalculator $lcs = null): array + public function diffToArray($from, $to, LongestCommonSubsequenceCalculator $lcs = null) { if (\is_string($from)) { $from = $this->splitStringByLines($from); @@ -171,7 +171,7 @@ public function diffToArray($from, $to, LongestCommonSubsequenceCalculator $lcs * * @return array */ - private function splitStringByLines(string $input): array + private function splitStringByLines($input) { return \preg_split('/(.*\R)/', $input, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); } @@ -182,7 +182,7 @@ private function splitStringByLines(string $input): array * * @return LongestCommonSubsequenceCalculator */ - private function selectLcsImplementation(array $from, array $to): LongestCommonSubsequenceCalculator + private function selectLcsImplementation(array $from, array $to) { // We do not want to use the time-efficient implementation if its memory // footprint will probably exceed this value. Note that the footprint @@ -219,7 +219,7 @@ private function calculateEstimatedFootprint(array $from, array $to) * * @return bool */ - private function detectUnmatchedLineEndings(array $diff): bool + private function detectUnmatchedLineEndings(array $diff) { $newLineBreaks = ['' => true]; $oldLineBreaks = ['' => true]; @@ -257,7 +257,7 @@ private function detectUnmatchedLineEndings(array $diff): bool return false; } - private function getLinebreak($line): string + private function getLinebreak($line) { if (!\is_string($line)) { return ''; @@ -279,7 +279,7 @@ private function getLinebreak($line): string return "\n"; } - private static function getArrayDiffParted(array &$from, array &$to): array + private static function getArrayDiffParted(array &$from, array &$to) { $start = []; $end = []; diff --git a/src/Exception/Exception.php b/src/Exception/Exception.php index 249a2ba0..380cb8af 100644 --- a/src/Exception/Exception.php +++ b/src/Exception/Exception.php @@ -1,4 +1,4 @@ -type = $type; $this->content = $content; } - public function getContent(): string + public function getContent() { return $this->content; } - public function getType(): int + public function getType() { return $this->type; } diff --git a/src/LongestCommonSubsequenceCalculator.php b/src/LongestCommonSubsequenceCalculator.php index 7eb1707e..240d9a53 100644 --- a/src/LongestCommonSubsequenceCalculator.php +++ b/src/LongestCommonSubsequenceCalculator.php @@ -1,4 +1,4 @@ -header = $header; } - public function getDiff(array $diff): string + public function getDiff(array $diff) { $buffer = \fopen('php://memory', 'r+b'); diff --git a/src/Output/DiffOutputBuilderInterface.php b/src/Output/DiffOutputBuilderInterface.php index 0e18f9f2..c6b5cbeb 100644 --- a/src/Output/DiffOutputBuilderInterface.php +++ b/src/Output/DiffOutputBuilderInterface.php @@ -1,4 +1,4 @@ -header = $header; $this->addLineNumbers = $addLineNumbers; } - public function getDiff(array $diff): string + public function getDiff(array $diff) { $buffer = \fopen('php://memory', 'r+b'); @@ -104,12 +104,12 @@ private function writeDiffChunked($output, array $diff, array $old) private function writeChunk( $output, array $diff, - int $diffStartIndex, - int $diffEndIndex, - int $fromStart, - int $fromRange, - int $toStart, - int $toRange + $diffStartIndex, + $diffEndIndex, + $fromStart, + $fromRange, + $toStart, + $toRange ) { if ($this->addLineNumbers) { \fwrite($output, '@@ -' . (1 + $fromStart)); @@ -144,7 +144,7 @@ private function writeChunk( } } - private function getChunkRange(array $diff, int $diffStartIndex, int $diffEndIndex): array + private function getChunkRange(array $diff, $diffStartIndex, $diffEndIndex) { $toRange = 0; $fromRange = 0; diff --git a/src/Parser.php b/src/Parser.php index dbbf6f4c..3e02b8e4 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -1,4 +1,4 @@ - + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff\Tests; + +use SebastianBergmann\Diff\Output\AbstractChunkOutputBuilder; + +/** + * @author SpacePossum + * @internal + */ +final class CommonChunksTestOutputBuilder extends AbstractChunkOutputBuilder +{ + public function getDiff(array $diff) + { + return ''; + } + + public function getChunks(array $diff, $lineThreshold) + { + return $this->getCommonChunks($diff, $lineThreshold); + } +} diff --git a/tests/DiffTest.php b/tests/DiffTest.php index fe5cb228..645a483a 100644 --- a/tests/DiffTest.php +++ b/tests/DiffTest.php @@ -1,4 +1,4 @@ -assertSame($expected, $this->differ->diff($from, $to, new TimeEfficientLongestCommonSubsequenceCalculator)); } @@ -84,7 +85,7 @@ public function testArrayRepresentationOfDiffCanBeRenderedUsingMemoryEfficientLc * @param string $to * @dataProvider textProvider */ - public function testTextRepresentationOfDiffCanBeRenderedUsingMemoryEfficientLcsImplementation(string $expected, string $from, string $to) + public function testTextRepresentationOfDiffCanBeRenderedUsingMemoryEfficientLcsImplementation($expected, $from, $to) { $this->assertSame($expected, $this->differ->diff($from, $to, new MemoryEfficientLongestCommonSubsequenceCalculator)); } @@ -96,7 +97,7 @@ public function testTextRepresentationOfDiffCanBeRenderedUsingMemoryEfficientLcs * @param string $header * @dataProvider headerProvider */ - public function testCustomHeaderCanBeUsed(string $expected, string $from, string $to, string $header) + public function testCustomHeaderCanBeUsed($expected, $from, $to, $header) { $differ = new Differ(new UnifiedDiffOutputBuilder($header)); @@ -149,7 +150,7 @@ public function testTypesOtherThanArrayAndStringCanBePassed() * @param Diff[] $expected * @dataProvider diffProvider */ - public function testParser(string $diff, array $expected) + public function testParser($diff, array $expected) { $parser = new Parser; $result = $parser->parse($diff); @@ -157,7 +158,7 @@ public function testParser(string $diff, array $expected) $this->assertEquals($expected, $result); } - public function arrayProvider(): array + public function arrayProvider() { return [ [ @@ -318,7 +319,7 @@ public function arrayProvider(): array ]; } - public function textProvider(): array + public function textProvider() { return [ [ @@ -409,7 +410,7 @@ public function textProvider(): array ]; } - public function diffProvider(): array + public function diffProvider() { $serialized_arr = <<assertSame($expected, $differ->diff($from, $to)); } - public function textForNoNonDiffLinesProvider(): array + public function textForNoNonDiffLinesProvider() { return [ [ @@ -475,6 +476,9 @@ public function textForNoNonDiffLinesProvider(): array ]; } + /** + * @requires PHPUnit 6.0 + */ public function testDiffToArrayInvalidFromType() { $this->expectException('\InvalidArgumentException'); @@ -483,6 +487,9 @@ public function testDiffToArrayInvalidFromType() $this->differ->diffToArray(null, ''); } + /** + * @requires PHPUnit 6.0 + */ public function testDiffInvalidToType() { $this->expectException('\InvalidArgumentException'); @@ -498,19 +505,9 @@ public function testDiffInvalidToType() * @param int $lineThreshold * @dataProvider provideGetCommonChunks */ - public function testGetCommonChunks(array $expected, string $from, string $to, int $lineThreshold = 5) + public function testGetCommonChunks(array $expected, $from, $to, $lineThreshold = 5) { - $output = new class extends AbstractChunkOutputBuilder { - public function getDiff(array $diff): string - { - return ''; - } - - public function getChunks(array $diff, $lineThreshold) - { - return $this->getCommonChunks($diff, $lineThreshold); - } - }; + $output = new CommonChunksTestOutputBuilder(); $this->assertSame( $expected, @@ -518,7 +515,7 @@ public function getChunks(array $diff, $lineThreshold) ); } - public function provideGetCommonChunks(): array + public function provideGetCommonChunks() { return[ 'same (with default threshold)' => [ @@ -623,7 +620,7 @@ public function provideGetCommonChunks(): array * @param string $input * @dataProvider provideSplitStringByLinesCases */ - public function testSplitStringByLines(array $expected, string $input) + public function testSplitStringByLines(array $expected, $input) { $reflection = new \ReflectionObject($this->differ); $method = $reflection->getMethod('splitStringByLines'); @@ -632,7 +629,7 @@ public function testSplitStringByLines(array $expected, string $input) $this->assertSame($expected, $method->invoke($this->differ, $input)); } - public function provideSplitStringByLinesCases(): array + public function provideSplitStringByLinesCases() { return [ [ @@ -710,7 +707,7 @@ public function testDiffWithLineNumbers($expected, $from, $to) $this->assertSame($expected, $differ->diff($from, $to)); } - public function provideDiffWithLineNumbers(): array + public function provideDiffWithLineNumbers() { return [ 'diff line 1 non_patch_compat' => [ @@ -1024,6 +1021,9 @@ public function testConstructorString() ); } + /** + * @requires PHPUnit 6.0 + */ public function testConstructorInvalidArgInt() { $this->expectException(InvalidArgumentException::class); @@ -1032,6 +1032,9 @@ public function testConstructorInvalidArgInt() new Differ(1); } + /** + * @requires PHPUnit 6.0 + */ public function testConstructorInvalidArgObject() { $this->expectException(InvalidArgumentException::class); diff --git a/tests/DifferTestTest.php b/tests/DifferTestTest.php index af60e26d..ab008216 100644 --- a/tests/DifferTestTest.php +++ b/tests/DifferTestTest.php @@ -1,4 +1,4 @@ - Date: Tue, 22 Aug 2017 09:35:10 +0200 Subject: [PATCH 02/25] Package name compat for packagist.org --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 6e01327f..6ebb57b5 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "PHP-CS-Fixer/diff", + "name": "php-cs-fixer/diff", "description": "sebastian/diff v2 backport support for PHP5.6", "keywords": ["diff"], "homepage": "https://github.com/PHP-CS-Fixer", From 864c4ab5eadf60eccb8af9b840e81b64b971382c Mon Sep 17 00:00:00 2001 From: SpacePossum Date: Tue, 22 Aug 2017 15:10:04 +0200 Subject: [PATCH 03/25] Switch NS --- composer.json | 2 +- src/Chunk.php | 2 +- src/Diff.php | 2 +- src/Differ.php | 6 ++-- src/Exception/Exception.php | 2 +- src/Exception/InvalidArgumentException.php | 2 +- src/Line.php | 2 +- src/LongestCommonSubsequenceCalculator.php | 2 +- ...ientLongestCommonSubsequenceCalculator.php | 2 +- src/Output/AbstractChunkOutputBuilder.php | 2 +- src/Output/DiffOnlyOutputBuilder.php | 2 +- src/Output/DiffOutputBuilderInterface.php | 2 +- src/Output/UnifiedDiffOutputBuilder.php | 2 +- src/Parser.php | 2 +- ...ientLongestCommonSubsequenceCalculator.php | 2 +- tests/ChunkTest.php | 2 +- tests/CommonChunksTestOutputBuilder.php | 4 +-- tests/DiffTest.php | 2 +- tests/DifferTest.php | 36 +++---------------- tests/DifferTestTest.php | 2 +- tests/LineTest.php | 2 +- tests/LongestCommonSubsequenceTest.php | 2 +- tests/MemoryEfficientImplementationTest.php | 2 +- tests/ParserTest.php | 2 +- tests/TimeEfficientImplementationTest.php | 2 +- 25 files changed, 31 insertions(+), 59 deletions(-) diff --git a/composer.json b/composer.json index 6ebb57b5..35c2ad2b 100644 --- a/composer.json +++ b/composer.json @@ -26,6 +26,6 @@ ] }, "autoload-dev": { - "psr-4": { "SebastianBergmann\\Diff\\Tests\\": "tests/" } + "psr-4": { "PhpCsFixer\\Diff\\Tests\\": "tests/" } } } diff --git a/src/Chunk.php b/src/Chunk.php index 2db5e7cd..317ed867 100644 --- a/src/Chunk.php +++ b/src/Chunk.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace SebastianBergmann\Diff; +namespace PhpCsFixer\Diff; final class Chunk { diff --git a/src/Diff.php b/src/Diff.php index 74d7c498..a458d274 100644 --- a/src/Diff.php +++ b/src/Diff.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace SebastianBergmann\Diff; +namespace PhpCsFixer\Diff; final class Diff { diff --git a/src/Differ.php b/src/Differ.php index a6eba763..cb1a5682 100644 --- a/src/Differ.php +++ b/src/Differ.php @@ -8,10 +8,10 @@ * file that was distributed with this source code. */ -namespace SebastianBergmann\Diff; +namespace PhpCsFixer\Diff; -use SebastianBergmann\Diff\Output\DiffOutputBuilderInterface; -use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder; +use PhpCsFixer\Diff\Output\DiffOutputBuilderInterface; +use PhpCsFixer\Diff\Output\UnifiedDiffOutputBuilder; /** * Diff implementation. diff --git a/src/Exception/Exception.php b/src/Exception/Exception.php index 380cb8af..ba4a6097 100644 --- a/src/Exception/Exception.php +++ b/src/Exception/Exception.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace SebastianBergmann\Diff; +namespace PhpCsFixer\Diff; interface Exception { diff --git a/src/Exception/InvalidArgumentException.php b/src/Exception/InvalidArgumentException.php index 9d90f081..e6946669 100644 --- a/src/Exception/InvalidArgumentException.php +++ b/src/Exception/InvalidArgumentException.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace SebastianBergmann\Diff; +namespace PhpCsFixer\Diff; class InvalidArgumentException extends \InvalidArgumentException implements Exception { diff --git a/src/Line.php b/src/Line.php index 5860cb08..df9d9567 100644 --- a/src/Line.php +++ b/src/Line.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace SebastianBergmann\Diff; +namespace PhpCsFixer\Diff; final class Line { diff --git a/src/LongestCommonSubsequenceCalculator.php b/src/LongestCommonSubsequenceCalculator.php index 240d9a53..d344851c 100644 --- a/src/LongestCommonSubsequenceCalculator.php +++ b/src/LongestCommonSubsequenceCalculator.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace SebastianBergmann\Diff; +namespace PhpCsFixer\Diff; interface LongestCommonSubsequenceCalculator { diff --git a/src/MemoryEfficientLongestCommonSubsequenceCalculator.php b/src/MemoryEfficientLongestCommonSubsequenceCalculator.php index 81ceaa3a..a54e190f 100644 --- a/src/MemoryEfficientLongestCommonSubsequenceCalculator.php +++ b/src/MemoryEfficientLongestCommonSubsequenceCalculator.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace SebastianBergmann\Diff; +namespace PhpCsFixer\Diff; final class MemoryEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator { diff --git a/src/Output/AbstractChunkOutputBuilder.php b/src/Output/AbstractChunkOutputBuilder.php index 9912f97f..6fec7045 100644 --- a/src/Output/AbstractChunkOutputBuilder.php +++ b/src/Output/AbstractChunkOutputBuilder.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace SebastianBergmann\Diff\Output; +namespace PhpCsFixer\Diff\Output; abstract class AbstractChunkOutputBuilder implements DiffOutputBuilderInterface { diff --git a/src/Output/DiffOnlyOutputBuilder.php b/src/Output/DiffOnlyOutputBuilder.php index a3b5f654..230aa2d4 100644 --- a/src/Output/DiffOnlyOutputBuilder.php +++ b/src/Output/DiffOnlyOutputBuilder.php @@ -7,7 +7,7 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace SebastianBergmann\Diff\Output; +namespace PhpCsFixer\Diff\Output; /** * Builds a diff string representation in a loose unified diff format diff --git a/src/Output/DiffOutputBuilderInterface.php b/src/Output/DiffOutputBuilderInterface.php index c6b5cbeb..64d6265a 100644 --- a/src/Output/DiffOutputBuilderInterface.php +++ b/src/Output/DiffOutputBuilderInterface.php @@ -7,7 +7,7 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace SebastianBergmann\Diff\Output; +namespace PhpCsFixer\Diff\Output; /** * Defines how an output builder should take a generated diff --git a/src/Output/UnifiedDiffOutputBuilder.php b/src/Output/UnifiedDiffOutputBuilder.php index 443eb2ef..3f22292e 100644 --- a/src/Output/UnifiedDiffOutputBuilder.php +++ b/src/Output/UnifiedDiffOutputBuilder.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace SebastianBergmann\Diff\Output; +namespace PhpCsFixer\Diff\Output; /** * Builds a diff string representation in unified diff format in chunks. diff --git a/src/Parser.php b/src/Parser.php index 3e02b8e4..87afb93f 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace SebastianBergmann\Diff; +namespace PhpCsFixer\Diff; /** * Unified diff parser. diff --git a/src/TimeEfficientLongestCommonSubsequenceCalculator.php b/src/TimeEfficientLongestCommonSubsequenceCalculator.php index 1ce09b0c..5e4d51d5 100644 --- a/src/TimeEfficientLongestCommonSubsequenceCalculator.php +++ b/src/TimeEfficientLongestCommonSubsequenceCalculator.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace SebastianBergmann\Diff; +namespace PhpCsFixer\Diff; final class TimeEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator { diff --git a/tests/ChunkTest.php b/tests/ChunkTest.php index 38a4d6c7..a6954d0c 100644 --- a/tests/ChunkTest.php +++ b/tests/ChunkTest.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace SebastianBergmann\Diff; +namespace PhpCsFixer\Diff; use PHPUnit\Framework\TestCase; diff --git a/tests/CommonChunksTestOutputBuilder.php b/tests/CommonChunksTestOutputBuilder.php index dac074c2..d803f6b8 100644 --- a/tests/CommonChunksTestOutputBuilder.php +++ b/tests/CommonChunksTestOutputBuilder.php @@ -8,9 +8,9 @@ * file that was distributed with this source code. */ -namespace SebastianBergmann\Diff\Tests; +namespace PhpCsFixer\Diff\Tests; -use SebastianBergmann\Diff\Output\AbstractChunkOutputBuilder; +use PhpCsFixer\Diff\Output\AbstractChunkOutputBuilder; /** * @author SpacePossum diff --git a/tests/DiffTest.php b/tests/DiffTest.php index 645a483a..587cc42f 100644 --- a/tests/DiffTest.php +++ b/tests/DiffTest.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace SebastianBergmann\Diff; +namespace PhpCsFixer\Diff; use PHPUnit\Framework\TestCase; diff --git a/tests/DifferTest.php b/tests/DifferTest.php index 896292fc..8b36cdc6 100644 --- a/tests/DifferTest.php +++ b/tests/DifferTest.php @@ -8,13 +8,12 @@ * file that was distributed with this source code. */ -namespace SebastianBergmann\Diff; +namespace PhpCsFixer\Diff; use PHPUnit\Framework\TestCase; -use SebastianBergmann\Diff\Output\AbstractChunkOutputBuilder; -use SebastianBergmann\Diff\Output\DiffOnlyOutputBuilder; -use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder; -use SebastianBergmann\Diff\Tests\CommonChunksTestOutputBuilder; +use PhpCsFixer\Diff\Output\DiffOnlyOutputBuilder; +use PhpCsFixer\Diff\Output\UnifiedDiffOutputBuilder; +use PhpCsFixer\Diff\Tests\CommonChunksTestOutputBuilder; /** * @covers SebastianBergmann\Diff\Differ @@ -145,19 +144,6 @@ public function testTypesOtherThanArrayAndStringCanBePassed() ); } - /** - * @param string $diff - * @param Diff[] $expected - * @dataProvider diffProvider - */ - public function testParser($diff, array $expected) - { - $parser = new Parser; - $result = $parser->parse($diff); - - $this->assertEquals($expected, $result); - } - public function arrayProvider() { return [ @@ -410,20 +396,6 @@ public function textProvider() ]; } - public function diffProvider() - { - $serialized_arr = << Date: Thu, 14 Sep 2017 18:47:45 +0200 Subject: [PATCH 04/25] Add 1.4 and backport 2.0 into one --- composer.json | 5 +- src/v1_4/Chunk.php | 103 +++++ src/v1_4/Diff.php | 73 ++++ src/v1_4/Differ.php | 399 ++++++++++++++++++ src/v1_4/LCS/LongestCommonSubsequence.php | 27 ++ ...LongestCommonSubsequenceImplementation.php | 95 +++++ ...LongestCommonSubsequenceImplementation.php | 74 ++++ src/v1_4/Line.php | 54 +++ src/v1_4/Parser.php | 110 +++++ src/{ => v2_0}/Chunk.php | 2 +- src/{ => v2_0}/Diff.php | 2 +- src/{ => v2_0}/Differ.php | 6 +- src/{ => v2_0}/Exception/Exception.php | 2 +- .../Exception/InvalidArgumentException.php | 2 +- src/{ => v2_0}/Line.php | 2 +- .../LongestCommonSubsequenceCalculator.php | 2 +- ...ientLongestCommonSubsequenceCalculator.php | 2 +- .../Output/AbstractChunkOutputBuilder.php | 2 +- .../Output/DiffOnlyOutputBuilder.php | 2 +- .../Output/DiffOutputBuilderInterface.php | 2 +- .../Output/UnifiedDiffOutputBuilder.php | 2 +- src/{ => v2_0}/Parser.php | 2 +- ...ientLongestCommonSubsequenceCalculator.php | 2 +- tests/v1_4/ChunkTest.php | 68 +++ tests/v1_4/DiffTest.php | 55 +++ tests/v1_4/DifferTest.php | 388 +++++++++++++++++ .../v1_4/LCS/LongestCommonSubsequenceTest.php | 198 +++++++++ .../LCS/MemoryEfficientImplementationTest.php | 22 + .../LCS/TimeEfficientImplementationTest.php | 22 + tests/v1_4/LineTest.php | 44 ++ tests/v1_4/ParserTest.php | 151 +++++++ tests/{ => v1_4}/fixtures/patch.txt | 0 tests/{ => v1_4}/fixtures/patch2.txt | 0 tests/{ => v2_0}/ChunkTest.php | 4 +- .../CommonChunksTestOutputBuilder.php | 4 +- tests/{ => v2_0}/DiffTest.php | 6 +- tests/{ => v2_0}/DifferTest.php | 28 +- tests/{ => v2_0}/DifferTestTest.php | 2 +- tests/{ => v2_0}/LineTest.php | 4 +- .../LongestCommonSubsequenceTest.php | 2 +- .../MemoryEfficientImplementationTest.php | 4 +- tests/{ => v2_0}/ParserTest.php | 10 +- .../TimeEfficientImplementationTest.php | 4 +- tests/v2_0/fixtures/patch.txt | 9 + tests/v2_0/fixtures/patch2.txt | 21 + 45 files changed, 1967 insertions(+), 51 deletions(-) create mode 100644 src/v1_4/Chunk.php create mode 100644 src/v1_4/Diff.php create mode 100644 src/v1_4/Differ.php create mode 100644 src/v1_4/LCS/LongestCommonSubsequence.php create mode 100644 src/v1_4/LCS/MemoryEfficientLongestCommonSubsequenceImplementation.php create mode 100644 src/v1_4/LCS/TimeEfficientLongestCommonSubsequenceImplementation.php create mode 100644 src/v1_4/Line.php create mode 100644 src/v1_4/Parser.php rename src/{ => v2_0}/Chunk.php (97%) rename src/{ => v2_0}/Diff.php (97%) rename src/{ => v2_0}/Differ.php (98%) rename src/{ => v2_0}/Exception/Exception.php (89%) rename src/{ => v2_0}/Exception/InvalidArgumentException.php (91%) rename src/{ => v2_0}/Line.php (95%) rename src/{ => v2_0}/LongestCommonSubsequenceCalculator.php (94%) rename src/{ => v2_0}/MemoryEfficientLongestCommonSubsequenceCalculator.php (98%) rename src/{ => v2_0}/Output/AbstractChunkOutputBuilder.php (97%) rename src/{ => v2_0}/Output/DiffOnlyOutputBuilder.php (97%) rename src/{ => v2_0}/Output/DiffOutputBuilderInterface.php (91%) rename src/{ => v2_0}/Output/UnifiedDiffOutputBuilder.php (99%) rename src/{ => v2_0}/Parser.php (98%) rename src/{ => v2_0}/TimeEfficientLongestCommonSubsequenceCalculator.php (98%) create mode 100644 tests/v1_4/ChunkTest.php create mode 100644 tests/v1_4/DiffTest.php create mode 100644 tests/v1_4/DifferTest.php create mode 100644 tests/v1_4/LCS/LongestCommonSubsequenceTest.php create mode 100644 tests/v1_4/LCS/MemoryEfficientImplementationTest.php create mode 100644 tests/v1_4/LCS/TimeEfficientImplementationTest.php create mode 100644 tests/v1_4/LineTest.php create mode 100644 tests/v1_4/ParserTest.php rename tests/{ => v1_4}/fixtures/patch.txt (100%) rename tests/{ => v1_4}/fixtures/patch2.txt (100%) rename tests/{ => v2_0}/ChunkTest.php (95%) rename tests/{ => v2_0}/CommonChunksTestOutputBuilder.php (85%) rename tests/{ => v2_0}/DiffTest.php (93%) rename tests/{ => v2_0}/DifferTest.php (96%) rename tests/{ => v2_0}/DifferTestTest.php (98%) rename tests/{ => v2_0}/LineTest.php (92%) rename tests/{ => v2_0}/LongestCommonSubsequenceTest.php (99%) rename tests/{ => v2_0}/MemoryEfficientImplementationTest.php (80%) rename tests/{ => v2_0}/ParserTest.php (95%) rename tests/{ => v2_0}/TimeEfficientImplementationTest.php (80%) create mode 100644 tests/v2_0/fixtures/patch.txt create mode 100644 tests/v2_0/fixtures/patch2.txt diff --git a/composer.json b/composer.json index 35c2ad2b..a2b8dc12 100644 --- a/composer.json +++ b/composer.json @@ -26,6 +26,9 @@ ] }, "autoload-dev": { - "psr-4": { "PhpCsFixer\\Diff\\Tests\\": "tests/" } + "psr-4": { + "PhpCsFixer\\Diff\\v1_4\\Tests\\": "tests/v1_4", + "PhpCsFixer\\Diff\\v2_0\\Tests\\": "tests/v2_0" + } } } diff --git a/src/v1_4/Chunk.php b/src/v1_4/Chunk.php new file mode 100644 index 00000000..b247e030 --- /dev/null +++ b/src/v1_4/Chunk.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v1_4; + +class Chunk +{ + /** + * @var int + */ + private $start; + + /** + * @var int + */ + private $startRange; + + /** + * @var int + */ + private $end; + + /** + * @var int + */ + private $endRange; + + /** + * @var array + */ + private $lines; + + /** + * @param int $start + * @param int $startRange + * @param int $end + * @param int $endRange + * @param array $lines + */ + public function __construct($start = 0, $startRange = 1, $end = 0, $endRange = 1, array $lines = array()) + { + $this->start = (int) $start; + $this->startRange = (int) $startRange; + $this->end = (int) $end; + $this->endRange = (int) $endRange; + $this->lines = $lines; + } + + /** + * @return int + */ + public function getStart() + { + return $this->start; + } + + /** + * @return int + */ + public function getStartRange() + { + return $this->startRange; + } + + /** + * @return int + */ + public function getEnd() + { + return $this->end; + } + + /** + * @return int + */ + public function getEndRange() + { + return $this->endRange; + } + + /** + * @return array + */ + public function getLines() + { + return $this->lines; + } + + /** + * @param array $lines + */ + public function setLines(array $lines) + { + $this->lines = $lines; + } +} diff --git a/src/v1_4/Diff.php b/src/v1_4/Diff.php new file mode 100644 index 00000000..695a519b --- /dev/null +++ b/src/v1_4/Diff.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v1_4; + +class Diff +{ + /** + * @var string + */ + private $from; + + /** + * @var string + */ + private $to; + + /** + * @var Chunk[] + */ + private $chunks; + + /** + * @param string $from + * @param string $to + * @param Chunk[] $chunks + */ + public function __construct($from, $to, array $chunks = array()) + { + $this->from = $from; + $this->to = $to; + $this->chunks = $chunks; + } + + /** + * @return string + */ + public function getFrom() + { + return $this->from; + } + + /** + * @return string + */ + public function getTo() + { + return $this->to; + } + + /** + * @return Chunk[] + */ + public function getChunks() + { + return $this->chunks; + } + + /** + * @param Chunk[] $chunks + */ + public function setChunks(array $chunks) + { + $this->chunks = $chunks; + } +} diff --git a/src/v1_4/Differ.php b/src/v1_4/Differ.php new file mode 100644 index 00000000..a6ef45e2 --- /dev/null +++ b/src/v1_4/Differ.php @@ -0,0 +1,399 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v1_4; + +use PhpCsFixer\Diff\v1_4\LCS\LongestCommonSubsequence; +use PhpCsFixer\Diff\v1_4\LCS\TimeEfficientImplementation; +use PhpCsFixer\Diff\v1_4\LCS\MemoryEfficientImplementation; + +/** + * Diff implementation. + */ +class Differ +{ + /** + * @var string + */ + private $header; + + /** + * @var bool + */ + private $showNonDiffLines; + + /** + * @param string $header + * @param bool $showNonDiffLines + */ + public function __construct($header = "--- Original\n+++ New\n", $showNonDiffLines = true) + { + $this->header = $header; + $this->showNonDiffLines = $showNonDiffLines; + } + + /** + * Returns the diff between two arrays or strings as string. + * + * @param array|string $from + * @param array|string $to + * @param LongestCommonSubsequence $lcs + * + * @return string + */ + public function diff($from, $to, LongestCommonSubsequence $lcs = null) + { + $from = $this->validateDiffInput($from); + $to = $this->validateDiffInput($to); + $diff = $this->diffToArray($from, $to, $lcs); + $old = $this->checkIfDiffInOld($diff); + $start = isset($old[0]) ? $old[0] : 0; + $end = \count($diff); + + if ($tmp = \array_search($end, $old)) { + $end = $tmp; + } + + return $this->getBuffer($diff, $old, $start, $end); + } + + /** + * Casts variable to string if it is not a string or array. + * + * @param mixed $input + * + * @return string + */ + private function validateDiffInput($input) + { + if (!\is_array($input) && !\is_string($input)) { + return (string) $input; + } + + return $input; + } + + /** + * Takes input of the diff array and returns the old array. + * Iterates through diff line by line, + * + * @param array $diff + * + * @return array + */ + private function checkIfDiffInOld(array $diff) + { + $inOld = false; + $i = 0; + $old = array(); + + foreach ($diff as $line) { + if ($line[1] === 0 /* OLD */) { + if ($inOld === false) { + $inOld = $i; + } + } elseif ($inOld !== false) { + if (($i - $inOld) > 5) { + $old[$inOld] = $i - 1; + } + + $inOld = false; + } + + ++$i; + } + + return $old; + } + + /** + * Generates buffer in string format, returning the patch. + * + * @param array $diff + * @param array $old + * @param int $start + * @param int $end + * + * @return string + */ + private function getBuffer(array $diff, array $old, $start, $end) + { + $buffer = $this->header; + + if (!isset($old[$start])) { + $buffer = $this->getDiffBufferElementNew($diff, $buffer, $start); + ++$start; + } + + for ($i = $start; $i < $end; $i++) { + if (isset($old[$i])) { + $i = $old[$i]; + $buffer = $this->getDiffBufferElementNew($diff, $buffer, $i); + } else { + $buffer = $this->getDiffBufferElement($diff, $buffer, $i); + } + } + + return $buffer; + } + + /** + * Gets individual buffer element. + * + * @param array $diff + * @param string $buffer + * @param int $diffIndex + * + * @return string + */ + private function getDiffBufferElement(array $diff, $buffer, $diffIndex) + { + if ($diff[$diffIndex][1] === 1 /* ADDED */) { + $buffer .= '+' . $diff[$diffIndex][0] . "\n"; + } elseif ($diff[$diffIndex][1] === 2 /* REMOVED */) { + $buffer .= '-' . $diff[$diffIndex][0] . "\n"; + } elseif ($this->showNonDiffLines === true) { + $buffer .= ' ' . $diff[$diffIndex][0] . "\n"; + } + + return $buffer; + } + + /** + * Gets individual buffer element with opening. + * + * @param array $diff + * @param string $buffer + * @param int $diffIndex + * + * @return string + */ + private function getDiffBufferElementNew(array $diff, $buffer, $diffIndex) + { + if ($this->showNonDiffLines === true) { + $buffer .= "@@ @@\n"; + } + + return $this->getDiffBufferElement($diff, $buffer, $diffIndex); + } + + /** + * Returns the diff between two arrays or strings as array. + * + * Each array element contains two elements: + * - [0] => mixed $token + * - [1] => 2|1|0 + * + * - 2: REMOVED: $token was removed from $from + * - 1: ADDED: $token was added to $from + * - 0: OLD: $token is not changed in $to + * + * @param array|string $from + * @param array|string $to + * @param LongestCommonSubsequence $lcs + * + * @return array + */ + public function diffToArray($from, $to, LongestCommonSubsequence $lcs = null) + { + if (\is_string($from)) { + $fromMatches = $this->getNewLineMatches($from); + $from = $this->splitStringByLines($from); + } elseif (\is_array($from)) { + $fromMatches = array(); + } else { + throw new \InvalidArgumentException('"from" must be an array or string.'); + } + + if (\is_string($to)) { + $toMatches = $this->getNewLineMatches($to); + $to = $this->splitStringByLines($to); + } elseif (\is_array($to)) { + $toMatches = array(); + } else { + throw new \InvalidArgumentException('"to" must be an array or string.'); + } + + list($from, $to, $start, $end) = self::getArrayDiffParted($from, $to); + + if ($lcs === null) { + $lcs = $this->selectLcsImplementation($from, $to); + } + + $common = $lcs->calculate(\array_values($from), \array_values($to)); + $diff = array(); + + if ($this->detectUnmatchedLineEndings($fromMatches, $toMatches)) { + $diff[] = array( + '#Warning: Strings contain different line endings!', + 0 + ); + } + + foreach ($start as $token) { + $diff[] = array($token, 0 /* OLD */); + } + + \reset($from); + \reset($to); + + foreach ($common as $token) { + while (($fromToken = \reset($from)) !== $token) { + $diff[] = array(\array_shift($from), 2 /* REMOVED */); + } + + while (($toToken = \reset($to)) !== $token) { + $diff[] = array(\array_shift($to), 1 /* ADDED */); + } + + $diff[] = array($token, 0 /* OLD */); + + \array_shift($from); + \array_shift($to); + } + + while (($token = \array_shift($from)) !== null) { + $diff[] = array($token, 2 /* REMOVED */); + } + + while (($token = \array_shift($to)) !== null) { + $diff[] = array($token, 1 /* ADDED */); + } + + foreach ($end as $token) { + $diff[] = array($token, 0 /* OLD */); + } + + return $diff; + } + + /** + * Get new strings denoting new lines from a given string. + * + * @param string $string + * + * @return array + */ + private function getNewLineMatches($string) + { + \preg_match_all('(\r\n|\r|\n)', $string, $stringMatches); + + return $stringMatches; + } + + /** + * Checks if input is string, if so it will split it line-by-line. + * + * @param string $input + * + * @return array + */ + private function splitStringByLines($input) + { + return \preg_split('(\r\n|\r|\n)', $input); + } + + /** + * @param array $from + * @param array $to + * + * @return LongestCommonSubsequence + */ + private function selectLcsImplementation(array $from, array $to) + { + // We do not want to use the time-efficient implementation if its memory + // footprint will probably exceed this value. Note that the footprint + // calculation is only an estimation for the matrix and the LCS method + // will typically allocate a bit more memory than this. + $memoryLimit = 100 * 1024 * 1024; + + if ($this->calculateEstimatedFootprint($from, $to) > $memoryLimit) { + return new MemoryEfficientImplementation; + } + + return new TimeEfficientImplementation; + } + + /** + * Calculates the estimated memory footprint for the DP-based method. + * + * @param array $from + * @param array $to + * + * @return int|float + */ + private function calculateEstimatedFootprint(array $from, array $to) + { + $itemSize = PHP_INT_SIZE === 4 ? 76 : 144; + + return $itemSize * \pow(\min(\count($from), \count($to)), 2); + } + + /** + * Returns true if line ends don't match on fromMatches and toMatches. + * + * @param array $fromMatches + * @param array $toMatches + * + * @return bool + */ + private function detectUnmatchedLineEndings(array $fromMatches, array $toMatches) + { + return isset($fromMatches[0], $toMatches[0]) && + \count($fromMatches[0]) === \count($toMatches[0]) && + $fromMatches[0] !== $toMatches[0]; + } + + /** + * @param array $from + * @param array $to + * + * @return array + */ + private static function getArrayDiffParted(array &$from, array &$to) + { + $start = array(); + $end = array(); + + \reset($to); + + foreach ($from as $k => $v) { + $toK = \key($to); + + if ($toK === $k && $v === $to[$k]) { + $start[$k] = $v; + + unset($from[$k], $to[$k]); + } else { + break; + } + } + + \end($from); + \end($to); + + do { + $fromK = \key($from); + $toK = \key($to); + + if (null === $fromK || null === $toK || \current($from) !== \current($to)) { + break; + } + + \prev($from); + \prev($to); + + $end = array($fromK => $from[$fromK]) + $end; + unset($from[$fromK], $to[$toK]); + } while (true); + + return array($from, $to, $start, $end); + } +} diff --git a/src/v1_4/LCS/LongestCommonSubsequence.php b/src/v1_4/LCS/LongestCommonSubsequence.php new file mode 100644 index 00000000..dad5a0cc --- /dev/null +++ b/src/v1_4/LCS/LongestCommonSubsequence.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v1_4\LCS; + +/** + * Interface for implementations of longest common subsequence calculation. + */ +interface LongestCommonSubsequence +{ + /** + * Calculates the longest common subsequence of two arrays. + * + * @param array $from + * @param array $to + * + * @return array + */ + public function calculate(array $from, array $to); +} diff --git a/src/v1_4/LCS/MemoryEfficientLongestCommonSubsequenceImplementation.php b/src/v1_4/LCS/MemoryEfficientLongestCommonSubsequenceImplementation.php new file mode 100644 index 00000000..66852525 --- /dev/null +++ b/src/v1_4/LCS/MemoryEfficientLongestCommonSubsequenceImplementation.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v1_4\LCS; + +/** + * Memory-efficient implementation of longest common subsequence calculation. + */ +class MemoryEfficientImplementation implements LongestCommonSubsequence +{ + /** + * Calculates the longest common subsequence of two arrays. + * + * @param array $from + * @param array $to + * + * @return array + */ + public function calculate(array $from, array $to) + { + $cFrom = \count($from); + $cTo = \count($to); + + if ($cFrom === 0) { + return array(); + } + + if ($cFrom === 1) { + if (\in_array($from[0], $to, true)) { + return array($from[0]); + } + + return array(); + } + + $i = (int) ($cFrom / 2); + $fromStart = \array_slice($from, 0, $i); + $fromEnd = \array_slice($from, $i); + $llB = $this->length($fromStart, $to); + $llE = $this->length(\array_reverse($fromEnd), \array_reverse($to)); + $jMax = 0; + $max = 0; + + for ($j = 0; $j <= $cTo; $j++) { + $m = $llB[$j] + $llE[$cTo - $j]; + + if ($m >= $max) { + $max = $m; + $jMax = $j; + } + } + + $toStart = \array_slice($to, 0, $jMax); + $toEnd = \array_slice($to, $jMax); + + return \array_merge( + $this->calculate($fromStart, $toStart), + $this->calculate($fromEnd, $toEnd) + ); + } + + /** + * @param array $from + * @param array $to + * + * @return array + */ + private function length(array $from, array $to) + { + $current = \array_fill(0, \count($to) + 1, 0); + $cFrom = \count($from); + $cTo = \count($to); + + for ($i = 0; $i < $cFrom; $i++) { + $prev = $current; + + for ($j = 0; $j < $cTo; $j++) { + if ($from[$i] === $to[$j]) { + $current[$j + 1] = $prev[$j] + 1; + } else { + $current[$j + 1] = \max($current[$j], $prev[$j + 1]); + } + } + } + + return $current; + } +} diff --git a/src/v1_4/LCS/TimeEfficientLongestCommonSubsequenceImplementation.php b/src/v1_4/LCS/TimeEfficientLongestCommonSubsequenceImplementation.php new file mode 100644 index 00000000..4acd68ef --- /dev/null +++ b/src/v1_4/LCS/TimeEfficientLongestCommonSubsequenceImplementation.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v1_4\LCS; + +/** + * Time-efficient implementation of longest common subsequence calculation. + */ +class TimeEfficientImplementation implements LongestCommonSubsequence +{ + /** + * Calculates the longest common subsequence of two arrays. + * + * @param array $from + * @param array $to + * + * @return array + */ + public function calculate(array $from, array $to) + { + $common = array(); + $fromLength = \count($from); + $toLength = \count($to); + $width = $fromLength + 1; + $matrix = new \SplFixedArray($width * ($toLength + 1)); + + for ($i = 0; $i <= $fromLength; ++$i) { + $matrix[$i] = 0; + } + + for ($j = 0; $j <= $toLength; ++$j) { + $matrix[$j * $width] = 0; + } + + for ($i = 1; $i <= $fromLength; ++$i) { + for ($j = 1; $j <= $toLength; ++$j) { + $o = ($j * $width) + $i; + $matrix[$o] = \max( + $matrix[$o - 1], + $matrix[$o - $width], + $from[$i - 1] === $to[$j - 1] ? $matrix[$o - $width - 1] + 1 : 0 + ); + } + } + + $i = $fromLength; + $j = $toLength; + + while ($i > 0 && $j > 0) { + if ($from[$i - 1] === $to[$j - 1]) { + $common[] = $from[$i - 1]; + --$i; + --$j; + } else { + $o = ($j * $width) + $i; + + if ($matrix[$o - $width] > $matrix[$o - 1]) { + --$j; + } else { + --$i; + } + } + } + + return \array_reverse($common); + } +} diff --git a/src/v1_4/Line.php b/src/v1_4/Line.php new file mode 100644 index 00000000..1187bcde --- /dev/null +++ b/src/v1_4/Line.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v1_4; + +class Line +{ + const ADDED = 1; + const REMOVED = 2; + const UNCHANGED = 3; + + /** + * @var int + */ + private $type; + + /** + * @var string + */ + private $content; + + /** + * @param int $type + * @param string $content + */ + public function __construct($type = self::UNCHANGED, $content = '') + { + $this->type = $type; + $this->content = $content; + } + + /** + * @return string + */ + public function getContent() + { + return $this->content; + } + + /** + * @return int + */ + public function getType() + { + return $this->type; + } +} diff --git a/src/v1_4/Parser.php b/src/v1_4/Parser.php new file mode 100644 index 00000000..6f8e75b5 --- /dev/null +++ b/src/v1_4/Parser.php @@ -0,0 +1,110 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v1_4; + +/** + * Unified diff parser. + */ +class Parser +{ + /** + * @param string $string + * + * @return Diff[] + */ + public function parse($string) + { + $lines = \preg_split('(\r\n|\r|\n)', $string); + + if (!empty($lines) && $lines[\count($lines) - 1] == '') { + \array_pop($lines); + } + + $lineCount = \count($lines); + $diffs = array(); + $diff = null; + $collected = array(); + + for ($i = 0; $i < $lineCount; ++$i) { + if (\preg_match('(^---\\s+(?P\\S+))', $lines[$i], $fromMatch) && + \preg_match('(^\\+\\+\\+\\s+(?P\\S+))', $lines[$i + 1], $toMatch)) { + if ($diff !== null) { + $this->parseFileDiff($diff, $collected); + + $diffs[] = $diff; + $collected = array(); + } + + $diff = new Diff($fromMatch['file'], $toMatch['file']); + + ++$i; + } else { + if (\preg_match('/^(?:diff --git |index [\da-f\.]+|[+-]{3} [ab])/', $lines[$i])) { + continue; + } + + $collected[] = $lines[$i]; + } + } + + if ($diff !== null && \count($collected)) { + $this->parseFileDiff($diff, $collected); + + $diffs[] = $diff; + } + + return $diffs; + } + + /** + * @param Diff $diff + * @param array $lines + */ + private function parseFileDiff(Diff $diff, array $lines) + { + $chunks = array(); + $chunk = null; + + foreach ($lines as $line) { + if (\preg_match('/^@@\s+-(?P\d+)(?:,\s*(?P\d+))?\s+\+(?P\d+)(?:,\s*(?P\d+))?\s+@@/', $line, $match)) { + $chunk = new Chunk( + $match['start'], + isset($match['startrange']) ? \max(1, $match['startrange']) : 1, + $match['end'], + isset($match['endrange']) ? \max(1, $match['endrange']) : 1 + ); + + $chunks[] = $chunk; + $diffLines = array(); + + continue; + } + + if (\preg_match('/^(?P[+ -])?(?P.*)/', $line, $match)) { + $type = Line::UNCHANGED; + + if ($match['type'] === '+') { + $type = Line::ADDED; + } elseif ($match['type'] === '-') { + $type = Line::REMOVED; + } + + $diffLines[] = new Line($type, $match['line']); + + if (null !== $chunk) { + $chunk->setLines($diffLines); + } + } + } + + $diff->setChunks($chunks); + } +} diff --git a/src/Chunk.php b/src/v2_0/Chunk.php similarity index 97% rename from src/Chunk.php rename to src/v2_0/Chunk.php index 317ed867..b80ddfcf 100644 --- a/src/Chunk.php +++ b/src/v2_0/Chunk.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff; +namespace PhpCsFixer\Diff\v2_0; final class Chunk { diff --git a/src/Diff.php b/src/v2_0/Diff.php similarity index 97% rename from src/Diff.php rename to src/v2_0/Diff.php index a458d274..e89b8e9d 100644 --- a/src/Diff.php +++ b/src/v2_0/Diff.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff; +namespace PhpCsFixer\Diff\v2_0; final class Diff { diff --git a/src/Differ.php b/src/v2_0/Differ.php similarity index 98% rename from src/Differ.php rename to src/v2_0/Differ.php index cb1a5682..ed66d867 100644 --- a/src/Differ.php +++ b/src/v2_0/Differ.php @@ -8,10 +8,10 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff; +namespace PhpCsFixer\Diff\v2_0; -use PhpCsFixer\Diff\Output\DiffOutputBuilderInterface; -use PhpCsFixer\Diff\Output\UnifiedDiffOutputBuilder; +use PhpCsFixer\Diff\v2_0\Output\DiffOutputBuilderInterface; +use PhpCsFixer\Diff\v2_0\Output\UnifiedDiffOutputBuilder; /** * Diff implementation. diff --git a/src/Exception/Exception.php b/src/v2_0/Exception/Exception.php similarity index 89% rename from src/Exception/Exception.php rename to src/v2_0/Exception/Exception.php index ba4a6097..b7e9e926 100644 --- a/src/Exception/Exception.php +++ b/src/v2_0/Exception/Exception.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff; +namespace PhpCsFixer\Diff\v2_0; interface Exception { diff --git a/src/Exception/InvalidArgumentException.php b/src/v2_0/Exception/InvalidArgumentException.php similarity index 91% rename from src/Exception/InvalidArgumentException.php rename to src/v2_0/Exception/InvalidArgumentException.php index e6946669..e1a0c0b5 100644 --- a/src/Exception/InvalidArgumentException.php +++ b/src/v2_0/Exception/InvalidArgumentException.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff; +namespace PhpCsFixer\Diff\v2_0; class InvalidArgumentException extends \InvalidArgumentException implements Exception { diff --git a/src/Line.php b/src/v2_0/Line.php similarity index 95% rename from src/Line.php rename to src/v2_0/Line.php index df9d9567..75d5ec85 100644 --- a/src/Line.php +++ b/src/v2_0/Line.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff; +namespace PhpCsFixer\Diff\v2_0; final class Line { diff --git a/src/LongestCommonSubsequenceCalculator.php b/src/v2_0/LongestCommonSubsequenceCalculator.php similarity index 94% rename from src/LongestCommonSubsequenceCalculator.php rename to src/v2_0/LongestCommonSubsequenceCalculator.php index d344851c..25eda02a 100644 --- a/src/LongestCommonSubsequenceCalculator.php +++ b/src/v2_0/LongestCommonSubsequenceCalculator.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff; +namespace PhpCsFixer\Diff\v2_0; interface LongestCommonSubsequenceCalculator { diff --git a/src/MemoryEfficientLongestCommonSubsequenceCalculator.php b/src/v2_0/MemoryEfficientLongestCommonSubsequenceCalculator.php similarity index 98% rename from src/MemoryEfficientLongestCommonSubsequenceCalculator.php rename to src/v2_0/MemoryEfficientLongestCommonSubsequenceCalculator.php index a54e190f..e6ce284f 100644 --- a/src/MemoryEfficientLongestCommonSubsequenceCalculator.php +++ b/src/v2_0/MemoryEfficientLongestCommonSubsequenceCalculator.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff; +namespace PhpCsFixer\Diff\v2_0; final class MemoryEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator { diff --git a/src/Output/AbstractChunkOutputBuilder.php b/src/v2_0/Output/AbstractChunkOutputBuilder.php similarity index 97% rename from src/Output/AbstractChunkOutputBuilder.php rename to src/v2_0/Output/AbstractChunkOutputBuilder.php index 6fec7045..d7d78d7c 100644 --- a/src/Output/AbstractChunkOutputBuilder.php +++ b/src/v2_0/Output/AbstractChunkOutputBuilder.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\Output; +namespace PhpCsFixer\Diff\v2_0\Output; abstract class AbstractChunkOutputBuilder implements DiffOutputBuilderInterface { diff --git a/src/Output/DiffOnlyOutputBuilder.php b/src/v2_0/Output/DiffOnlyOutputBuilder.php similarity index 97% rename from src/Output/DiffOnlyOutputBuilder.php rename to src/v2_0/Output/DiffOnlyOutputBuilder.php index 230aa2d4..51cb2dec 100644 --- a/src/Output/DiffOnlyOutputBuilder.php +++ b/src/v2_0/Output/DiffOnlyOutputBuilder.php @@ -7,7 +7,7 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\Output; +namespace PhpCsFixer\Diff\v2_0\Output; /** * Builds a diff string representation in a loose unified diff format diff --git a/src/Output/DiffOutputBuilderInterface.php b/src/v2_0/Output/DiffOutputBuilderInterface.php similarity index 91% rename from src/Output/DiffOutputBuilderInterface.php rename to src/v2_0/Output/DiffOutputBuilderInterface.php index 64d6265a..b52b8ac8 100644 --- a/src/Output/DiffOutputBuilderInterface.php +++ b/src/v2_0/Output/DiffOutputBuilderInterface.php @@ -7,7 +7,7 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\Output; +namespace PhpCsFixer\Diff\v2_0\Output; /** * Defines how an output builder should take a generated diff --git a/src/Output/UnifiedDiffOutputBuilder.php b/src/v2_0/Output/UnifiedDiffOutputBuilder.php similarity index 99% rename from src/Output/UnifiedDiffOutputBuilder.php rename to src/v2_0/Output/UnifiedDiffOutputBuilder.php index 3f22292e..90ee2dc2 100644 --- a/src/Output/UnifiedDiffOutputBuilder.php +++ b/src/v2_0/Output/UnifiedDiffOutputBuilder.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\Output; +namespace PhpCsFixer\Diff\v2_0\Output; /** * Builds a diff string representation in unified diff format in chunks. diff --git a/src/Parser.php b/src/v2_0/Parser.php similarity index 98% rename from src/Parser.php rename to src/v2_0/Parser.php index 87afb93f..9c0ee839 100644 --- a/src/Parser.php +++ b/src/v2_0/Parser.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff; +namespace PhpCsFixer\Diff\v2_0; /** * Unified diff parser. diff --git a/src/TimeEfficientLongestCommonSubsequenceCalculator.php b/src/v2_0/TimeEfficientLongestCommonSubsequenceCalculator.php similarity index 98% rename from src/TimeEfficientLongestCommonSubsequenceCalculator.php rename to src/v2_0/TimeEfficientLongestCommonSubsequenceCalculator.php index 5e4d51d5..51489afa 100644 --- a/src/TimeEfficientLongestCommonSubsequenceCalculator.php +++ b/src/v2_0/TimeEfficientLongestCommonSubsequenceCalculator.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff; +namespace PhpCsFixer\Diff\v2_0; final class TimeEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator { diff --git a/tests/v1_4/ChunkTest.php b/tests/v1_4/ChunkTest.php new file mode 100644 index 00000000..b7fe72f0 --- /dev/null +++ b/tests/v1_4/ChunkTest.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v1_4; + +use PHPUnit\Framework\TestCase; + +/** + * @covers \PhpCsFixer\Diff\v1_4\Chunk + */ +class ChunkTest extends TestCase +{ + /** + * @var Chunk + */ + private $chunk; + + protected function setUp() + { + $this->chunk = new Chunk; + } + + public function testCanBeCreatedWithoutArguments() + { + $this->assertInstanceOf('PhpCsFixer\Diff\v1_4\Chunk', $this->chunk); + } + + public function testStartCanBeRetrieved() + { + $this->assertEquals(0, $this->chunk->getStart()); + } + + public function testStartRangeCanBeRetrieved() + { + $this->assertEquals(1, $this->chunk->getStartRange()); + } + + public function testEndCanBeRetrieved() + { + $this->assertEquals(0, $this->chunk->getEnd()); + } + + public function testEndRangeCanBeRetrieved() + { + $this->assertEquals(1, $this->chunk->getEndRange()); + } + + public function testLinesCanBeRetrieved() + { + $this->assertEquals(array(), $this->chunk->getLines()); + } + + public function testLinesCanBeSet() + { + $this->assertEquals(array(), $this->chunk->getLines()); + + $testValue = array('line0', 'line1'); + $this->chunk->setLines($testValue); + $this->assertEquals($testValue, $this->chunk->getLines()); + } +} diff --git a/tests/v1_4/DiffTest.php b/tests/v1_4/DiffTest.php new file mode 100644 index 00000000..7be70579 --- /dev/null +++ b/tests/v1_4/DiffTest.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v1_4; + +use PHPUnit\Framework\TestCase; + +/** + * @covers \PhpCsFixer\Diff\v1_4\Diff + * + * @uses \PhpCsFixer\Diff\v1_4\Chunk + */ +final class DiffTest extends TestCase +{ + public function testGettersAfterConstructionWithDefault() + { + $from = 'line1a'; + $to = 'line2a'; + $diff = new Diff($from, $to); + + $this->assertSame($from, $diff->getFrom()); + $this->assertSame($to, $diff->getTo()); + $this->assertSame(array(), $diff->getChunks(), 'Expect chunks to be default value "array()".'); + } + + public function testGettersAfterConstructionWithChunks() + { + $from = 'line1b'; + $to = 'line2b'; + $chunks = array(new Chunk(), new Chunk(2, 3)); + + $diff = new Diff($from, $to, $chunks); + + $this->assertSame($from, $diff->getFrom()); + $this->assertSame($to, $diff->getTo()); + $this->assertSame($chunks, $diff->getChunks(), 'Expect chunks to be passed value.'); + } + + public function testSetChunksAfterConstruction() + { + $diff = new Diff('line1c', 'line2c'); + $this->assertSame(array(), $diff->getChunks(), 'Expect chunks to be default value "array()".'); + + $chunks = array(new Chunk(), new Chunk(2, 3)); + $diff->setChunks($chunks); + $this->assertSame($chunks, $diff->getChunks(), 'Expect chunks to be passed value.'); + } +} diff --git a/tests/v1_4/DifferTest.php b/tests/v1_4/DifferTest.php new file mode 100644 index 00000000..6f9c41bd --- /dev/null +++ b/tests/v1_4/DifferTest.php @@ -0,0 +1,388 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v1_4; + +use PhpCsFixer\Diff\v1_4\LCS\MemoryEfficientImplementation; +use PhpCsFixer\Diff\v1_4\LCS\TimeEfficientImplementation; +use PHPUnit\Framework\TestCase; + +/** + * @covers \PhpCsFixer\Diff\v1_4\Differ + * + * @uses \PhpCsFixer\Diff\v1_4\LCS\MemoryEfficientImplementation + * @uses \PhpCsFixer\Diff\v1_4\LCS\TimeEfficientImplementation + * @uses \PhpCsFixer\Diff\v1_4\Chunk + * @uses \PhpCsFixer\Diff\v1_4\Diff + * @uses \PhpCsFixer\Diff\v1_4\Line + * @uses \PhpCsFixer\Diff\v1_4\Parser + */ +class DifferTest extends TestCase +{ + const REMOVED = 2; + const ADDED = 1; + const OLD = 0; + + /** + * @var Differ + */ + private $differ; + + protected function setUp() + { + $this->differ = new Differ; + } + + /** + * @param array $expected + * @param string|array $from + * @param string|array $to + * @dataProvider arrayProvider + */ + public function testArrayRepresentationOfDiffCanBeRenderedUsingTimeEfficientLcsImplementation(array $expected, $from, $to) + { + $this->assertEquals($expected, $this->differ->diffToArray($from, $to, new TimeEfficientImplementation)); + } + + /** + * @param string $expected + * @param string $from + * @param string $to + * @dataProvider textProvider + */ + public function testTextRepresentationOfDiffCanBeRenderedUsingTimeEfficientLcsImplementation($expected, $from, $to) + { + $this->assertEquals($expected, $this->differ->diff($from, $to, new TimeEfficientImplementation)); + } + + /** + * @param array $expected + * @param string|array $from + * @param string|array $to + * @dataProvider arrayProvider + */ + public function testArrayRepresentationOfDiffCanBeRenderedUsingMemoryEfficientLcsImplementation(array $expected, $from, $to) + { + $this->assertEquals($expected, $this->differ->diffToArray($from, $to, new MemoryEfficientImplementation)); + } + + /** + * @param string $expected + * @param string $from + * @param string $to + * @dataProvider textProvider + */ + public function testTextRepresentationOfDiffCanBeRenderedUsingMemoryEfficientLcsImplementation($expected, $from, $to) + { + $this->assertEquals($expected, $this->differ->diff($from, $to, new MemoryEfficientImplementation)); + } + + public function testCustomHeaderCanBeUsed() + { + $differ = new Differ('CUSTOM HEADER'); + + $this->assertEquals( + "CUSTOM HEADER@@ @@\n-a\n+b\n", + $differ->diff('a', 'b') + ); + } + + public function testTypesOtherThanArrayAndStringCanBePassed() + { + $this->assertEquals( + "--- Original\n+++ New\n@@ @@\n-1\n+2\n", + $this->differ->diff(1, 2) + ); + } + + public function arrayProvider() + { + return array( + array( + array( + array('a', self::REMOVED), + array('b', self::ADDED) + ), + 'a', + 'b' + ), + array( + array( + array('ba', self::REMOVED), + array('bc', self::ADDED) + ), + 'ba', + 'bc' + ), + array( + array( + array('ab', self::REMOVED), + array('cb', self::ADDED) + ), + 'ab', + 'cb' + ), + array( + array( + array('abc', self::REMOVED), + array('adc', self::ADDED) + ), + 'abc', + 'adc' + ), + array( + array( + array('ab', self::REMOVED), + array('abc', self::ADDED) + ), + 'ab', + 'abc' + ), + array( + array( + array('bc', self::REMOVED), + array('abc', self::ADDED) + ), + 'bc', + 'abc' + ), + array( + array( + array('abc', self::REMOVED), + array('abbc', self::ADDED) + ), + 'abc', + 'abbc' + ), + array( + array( + array('abcdde', self::REMOVED), + array('abcde', self::ADDED) + ), + 'abcdde', + 'abcde' + ), + 'same start' => array( + array( + array(17, self::OLD), + array('b', self::REMOVED), + array('d', self::ADDED), + ), + array(30 => 17, 'a' => 'b'), + array(30 => 17, 'c' => 'd'), + ), + 'same end' => array( + array( + array(1, self::REMOVED), + array(2, self::ADDED), + array('b', self::OLD), + ), + array(1 => 1, 'a' => 'b'), + array(1 => 2, 'a' => 'b'), + ), + 'same start (2), same end (1)' => array( + array( + array(17, self::OLD), + array(2, self::OLD), + array(4, self::REMOVED), + array('a', self::ADDED), + array(5, self::ADDED), + array('x', self::OLD), + ), + array(30 => 17, 1 => 2, 2 => 4, 'z' => 'x'), + array(30 => 17, 1 => 2, 3 => 'a', 2 => 5, 'z' => 'x'), + ), + 'same' => array( + array( + array('x', self::OLD), + ), + array('z' => 'x'), + array('z' => 'x'), + ), + 'diff' => array( + array( + array('y', self::REMOVED), + array('x', self::ADDED), + ), + array('x' => 'y'), + array('z' => 'x'), + ), + 'diff 2' => array( + array( + array('y', self::REMOVED), + array('b', self::REMOVED), + array('x', self::ADDED), + array('d', self::ADDED), + ), + array('x' => 'y', 'a' => 'b'), + array('z' => 'x', 'c' => 'd'), + ), + 'test line diff detection' => array( + array( + array( + '#Warning: Strings contain different line endings!', + self::OLD, + ), + array( + 'assertSame($expected, $differ->diff($from, $to)); + } + + public function textForNoNonDiffLinesProvider() + { + return array( + array( + '', 'a', 'a' + ), + array( + "-A\n+C\n", + "A\n\n\nB", + "C\n\n\nB", + ), + ); + } + + /** + * @requires PHPUnit 5.7 + */ + public function testDiffToArrayInvalidFromType() + { + $differ = new Differ; + + $this->expectException('\InvalidArgumentException'); + $this->expectExceptionMessageRegExp('#^"from" must be an array or string\.$#'); + + $differ->diffToArray(null, ''); + } + + /** + * @requires PHPUnit 5.7 + */ + public function testDiffInvalidToType() + { + $differ = new Differ; + + $this->expectException('\InvalidArgumentException'); + $this->expectExceptionMessageRegExp('#^"to" must be an array or string\.$#'); + + $differ->diffToArray('', new \stdClass); + } +} diff --git a/tests/v1_4/LCS/LongestCommonSubsequenceTest.php b/tests/v1_4/LCS/LongestCommonSubsequenceTest.php new file mode 100644 index 00000000..d43cb036 --- /dev/null +++ b/tests/v1_4/LCS/LongestCommonSubsequenceTest.php @@ -0,0 +1,198 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v1_4\LCS; + +use PHPUnit\Framework\TestCase; + +abstract class LongestCommonSubsequenceTest extends TestCase +{ + /** + * @var LongestCommonSubsequence + */ + private $implementation; + + /** + * @var string + */ + private $memoryLimit; + + /** + * @var int[] + */ + private $stress_sizes = array(1, 2, 3, 100, 500, 1000, 2000); + + protected function setUp() + { + $this->memoryLimit = \ini_get('memory_limit'); + \ini_set('memory_limit', '256M'); + + $this->implementation = $this->createImplementation(); + } + + /** + * @return LongestCommonSubsequence + */ + abstract protected function createImplementation(); + + protected function tearDown() + { + \ini_set('memory_limit', $this->memoryLimit); + } + + public function testBothEmpty() + { + $from = array(); + $to = array(); + $common = $this->implementation->calculate($from, $to); + + $this->assertEquals(array(), $common); + } + + public function testIsStrictComparison() + { + $from = array( + false, 0, 0.0, '', null, array(), + true, 1, 1.0, 'foo', array('foo', 'bar'), array('foo' => 'bar') + ); + $to = $from; + $common = $this->implementation->calculate($from, $to); + + $this->assertEquals($from, $common); + + $to = array( + false, false, false, false, false, false, + true, true, true, true, true, true + ); + + $expected = array( + false, + true, + ); + + $common = $this->implementation->calculate($from, $to); + + $this->assertEquals($expected, $common); + } + + public function testEqualSequences() + { + foreach ($this->stress_sizes as $size) { + $range = \range(1, $size); + $from = $range; + $to = $range; + $common = $this->implementation->calculate($from, $to); + + $this->assertEquals($range, $common); + } + } + + public function testDistinctSequences() + { + $from = array('A'); + $to = array('B'); + $common = $this->implementation->calculate($from, $to); + $this->assertEquals(array(), $common); + + $from = array('A', 'B', 'C'); + $to = array('D', 'E', 'F'); + $common = $this->implementation->calculate($from, $to); + $this->assertEquals(array(), $common); + + foreach ($this->stress_sizes as $size) { + $from = \range(1, $size); + $to = \range($size + 1, $size * 2); + $common = $this->implementation->calculate($from, $to); + $this->assertEquals(array(), $common); + } + } + + public function testCommonSubsequence() + { + $from = array('A', 'C', 'E', 'F', 'G'); + $to = array('A', 'B', 'D', 'E', 'H'); + $expected = array('A', 'E'); + $common = $this->implementation->calculate($from, $to); + $this->assertEquals($expected, $common); + + $from = array('A', 'C', 'E', 'F', 'G'); + $to = array('B', 'C', 'D', 'E', 'F', 'H'); + $expected = array('C', 'E', 'F'); + $common = $this->implementation->calculate($from, $to); + $this->assertEquals($expected, $common); + + foreach ($this->stress_sizes as $size) { + $from = $size < 2 ? array(1) : \range(1, $size + 1, 2); + $to = $size < 3 ? array(1) : \range(1, $size + 1, 3); + $expected = $size < 6 ? array(1) : \range(1, $size + 1, 6); + $common = $this->implementation->calculate($from, $to); + + $this->assertEquals($expected, $common); + } + } + + public function testSingleElementSubsequenceAtStart() + { + foreach ($this->stress_sizes as $size) { + $from = \range(1, $size); + $to = \array_slice($from, 0, 1); + $common = $this->implementation->calculate($from, $to); + + $this->assertEquals($to, $common); + } + } + + public function testSingleElementSubsequenceAtMiddle() + { + foreach ($this->stress_sizes as $size) { + $from = \range(1, $size); + $to = \array_slice($from, (int) $size / 2, 1); + $common = $this->implementation->calculate($from, $to); + + $this->assertEquals($to, $common); + } + } + + public function testSingleElementSubsequenceAtEnd() + { + foreach ($this->stress_sizes as $size) { + $from = \range(1, $size); + $to = \array_slice($from, $size - 1, 1); + $common = $this->implementation->calculate($from, $to); + + $this->assertEquals($to, $common); + } + } + + public function testReversedSequences() + { + $from = array('A', 'B'); + $to = array('B', 'A'); + $expected = array('A'); + $common = $this->implementation->calculate($from, $to); + $this->assertEquals($expected, $common); + + foreach ($this->stress_sizes as $size) { + $from = \range(1, $size); + $to = \array_reverse($from); + $common = $this->implementation->calculate($from, $to); + + $this->assertEquals(array(1), $common); + } + } + + public function testStrictTypeCalculate() + { + $diff = $this->implementation->calculate(array('5'), array('05')); + + $this->assertInternalType('array', $diff); + $this->assertCount(0, $diff); + } +} diff --git a/tests/v1_4/LCS/MemoryEfficientImplementationTest.php b/tests/v1_4/LCS/MemoryEfficientImplementationTest.php new file mode 100644 index 00000000..98b0b266 --- /dev/null +++ b/tests/v1_4/LCS/MemoryEfficientImplementationTest.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v1_4\LCS; + +/** + * @covers \PhpCsFixer\Diff\v1_4\LCS\MemoryEfficientImplementation + */ +class MemoryEfficientImplementationTest extends LongestCommonSubsequenceTest +{ + protected function createImplementation() + { + return new MemoryEfficientImplementation; + } +} diff --git a/tests/v1_4/LCS/TimeEfficientImplementationTest.php b/tests/v1_4/LCS/TimeEfficientImplementationTest.php new file mode 100644 index 00000000..e78e04a8 --- /dev/null +++ b/tests/v1_4/LCS/TimeEfficientImplementationTest.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v1_4\LCS; + +/** + * @covers \PhpCsFixer\Diff\v1_4\LCS\TimeEfficientImplementation + */ +class TimeEfficientImplementationTest extends LongestCommonSubsequenceTest +{ + protected function createImplementation() + { + return new TimeEfficientImplementation; + } +} diff --git a/tests/v1_4/LineTest.php b/tests/v1_4/LineTest.php new file mode 100644 index 00000000..fd73f7e2 --- /dev/null +++ b/tests/v1_4/LineTest.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v1_4; + +use PHPUnit\Framework\TestCase; + +/** + * @covers \PhpCsFixer\Diff\v1_4\Line + */ +class LineTest extends TestCase +{ + /** + * @var Line + */ + private $line; + + protected function setUp() + { + $this->line = new Line; + } + + public function testCanBeCreatedWithoutArguments() + { + $this->assertInstanceOf('PhpCsFixer\Diff\v1_4\Line', $this->line); + } + + public function testTypeCanBeRetrieved() + { + $this->assertEquals(Line::UNCHANGED, $this->line->getType()); + } + + public function testContentCanBeRetrieved() + { + $this->assertEquals('', $this->line->getContent()); + } +} diff --git a/tests/v1_4/ParserTest.php b/tests/v1_4/ParserTest.php new file mode 100644 index 00000000..01482b78 --- /dev/null +++ b/tests/v1_4/ParserTest.php @@ -0,0 +1,151 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v1_4; + +use PHPUnit\Framework\TestCase; + +/** + * @covers \PhpCsFixer\Diff\v1_4\Parser + * + * @uses \PhpCsFixer\Diff\v1_4\Chunk + * @uses \PhpCsFixer\Diff\v1_4\Diff + * @uses \PhpCsFixer\Diff\v1_4\Line + */ +class ParserTest extends TestCase +{ + /** + * @var Parser + */ + private $parser; + + protected function setUp() + { + $this->parser = new Parser; + } + + public function testParse() + { + $content = \file_get_contents(__DIR__ . '/fixtures/patch.txt'); + + $diffs = $this->parser->parse($content); + + $this->assertInternalType('array', $diffs); + $this->assertContainsOnlyInstancesOf('PhpCsFixer\Diff\v1_4\Diff', $diffs); + $this->assertCount(1, $diffs); + + $chunks = $diffs[0]->getChunks(); + $this->assertInternalType('array', $chunks); + $this->assertContainsOnlyInstancesOf('PhpCsFixer\Diff\v1_4\Chunk', $chunks); + + $this->assertCount(1, $chunks); + + $this->assertEquals(20, $chunks[0]->getStart()); + + $this->assertCount(4, $chunks[0]->getLines()); + } + + public function testParseWithMultipleChunks() + { + $content = \file_get_contents(__DIR__ . '/fixtures/patch2.txt'); + + $diffs = $this->parser->parse($content); + + $this->assertCount(1, $diffs); + + $chunks = $diffs[0]->getChunks(); + $this->assertCount(3, $chunks); + + $this->assertEquals(20, $chunks[0]->getStart()); + $this->assertEquals(320, $chunks[1]->getStart()); + $this->assertEquals(600, $chunks[2]->getStart()); + + $this->assertCount(5, $chunks[0]->getLines()); + $this->assertCount(5, $chunks[1]->getLines()); + $this->assertCount(4, $chunks[2]->getLines()); + } + + public function testParseWithRemovedLines() + { + $content = <<parser->parse($content); + $this->assertInternalType('array', $diffs); + $this->assertContainsOnlyInstancesOf('PhpCsFixer\Diff\v1_4\Diff', $diffs); + $this->assertCount(1, $diffs); + + $chunks = $diffs[0]->getChunks(); + + $this->assertInternalType('array', $chunks); + $this->assertContainsOnlyInstancesOf('PhpCsFixer\Diff\v1_4\Chunk', $chunks); + $this->assertCount(1, $chunks); + + $chunk = $chunks[0]; + $this->assertSame(49, $chunk->getStart()); + $this->assertSame(49, $chunk->getEnd()); + $this->assertSame(9, $chunk->getStartRange()); + $this->assertSame(8, $chunk->getEndRange()); + + $lines = $chunk->getLines(); + $this->assertInternalType('array', $lines); + $this->assertContainsOnlyInstancesOf('PhpCsFixer\Diff\v1_4\Line', $lines); + $this->assertCount(2, $lines); + + /** @var Line $line */ + $line = $lines[0]; + $this->assertSame('A', $line->getContent()); + $this->assertSame(Line::UNCHANGED, $line->getType()); + + $line = $lines[1]; + $this->assertSame('B', $line->getContent()); + $this->assertSame(Line::REMOVED, $line->getType()); + } + + public function testParseDiffForMulitpleFiles() + { + $content = <<parser->parse($content); + $this->assertCount(2, $diffs); + + /** @var Diff $diff */ + $diff = $diffs[0]; + $this->assertSame('a/Test.txt', $diff->getFrom()); + $this->assertSame('b/Test.txt', $diff->getTo()); + $this->assertCount(1, $diff->getChunks()); + + $diff = $diffs[1]; + $this->assertSame('a/Test2.txt', $diff->getFrom()); + $this->assertSame('b/Test2.txt', $diff->getTo()); + $this->assertCount(1, $diff->getChunks()); + } +} diff --git a/tests/fixtures/patch.txt b/tests/v1_4/fixtures/patch.txt similarity index 100% rename from tests/fixtures/patch.txt rename to tests/v1_4/fixtures/patch.txt diff --git a/tests/fixtures/patch2.txt b/tests/v1_4/fixtures/patch2.txt similarity index 100% rename from tests/fixtures/patch2.txt rename to tests/v1_4/fixtures/patch2.txt diff --git a/tests/ChunkTest.php b/tests/v2_0/ChunkTest.php similarity index 95% rename from tests/ChunkTest.php rename to tests/v2_0/ChunkTest.php index a6954d0c..6fa49e80 100644 --- a/tests/ChunkTest.php +++ b/tests/v2_0/ChunkTest.php @@ -8,12 +8,12 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff; +namespace PhpCsFixer\Diff\v2_0; use PHPUnit\Framework\TestCase; /** - * @covers SebastianBergmann\Diff\Chunk + * @covers \PhpCsFixer\Diff\v2_0\Chunk */ final class ChunkTest extends TestCase { diff --git a/tests/CommonChunksTestOutputBuilder.php b/tests/v2_0/CommonChunksTestOutputBuilder.php similarity index 85% rename from tests/CommonChunksTestOutputBuilder.php rename to tests/v2_0/CommonChunksTestOutputBuilder.php index d803f6b8..73050b5c 100644 --- a/tests/CommonChunksTestOutputBuilder.php +++ b/tests/v2_0/CommonChunksTestOutputBuilder.php @@ -8,9 +8,9 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\Tests; +namespace PhpCsFixer\Diff\v2_0\Tests; -use PhpCsFixer\Diff\Output\AbstractChunkOutputBuilder; +use PhpCsFixer\Diff\v2_0\Output\AbstractChunkOutputBuilder; /** * @author SpacePossum diff --git a/tests/DiffTest.php b/tests/v2_0/DiffTest.php similarity index 93% rename from tests/DiffTest.php rename to tests/v2_0/DiffTest.php index 587cc42f..ac2f8ffc 100644 --- a/tests/DiffTest.php +++ b/tests/v2_0/DiffTest.php @@ -8,14 +8,14 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff; +namespace PhpCsFixer\Diff\v2_0; use PHPUnit\Framework\TestCase; /** - * @covers SebastianBergmann\Diff\Diff + * @covers \PhpCsFixer\Diff\v2_0\Diff * - * @uses SebastianBergmann\Diff\Chunk + * @uses \PhpCsFixer\Diff\v2_0\Chunk */ final class DiffTest extends TestCase { diff --git a/tests/DifferTest.php b/tests/v2_0/DifferTest.php similarity index 96% rename from tests/DifferTest.php rename to tests/v2_0/DifferTest.php index 8b36cdc6..feb6cd8c 100644 --- a/tests/DifferTest.php +++ b/tests/v2_0/DifferTest.php @@ -8,25 +8,25 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff; +namespace PhpCsFixer\Diff\v2_0; use PHPUnit\Framework\TestCase; -use PhpCsFixer\Diff\Output\DiffOnlyOutputBuilder; -use PhpCsFixer\Diff\Output\UnifiedDiffOutputBuilder; -use PhpCsFixer\Diff\Tests\CommonChunksTestOutputBuilder; +use PhpCsFixer\Diff\v2_0\Output\DiffOnlyOutputBuilder; +use PhpCsFixer\Diff\v2_0\Output\UnifiedDiffOutputBuilder; +use PhpCsFixer\Diff\v2_0\Tests\CommonChunksTestOutputBuilder; /** - * @covers SebastianBergmann\Diff\Differ - * @covers SebastianBergmann\Diff\Output\AbstractChunkOutputBuilder - * @covers SebastianBergmann\Diff\Output\DiffOnlyOutputBuilder - * @covers SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder + * @covers \PhpCsFixer\Diff\v2_0\Differ + * @covers \PhpCsFixer\Diff\v2_0\Output\AbstractChunkOutputBuilder + * @covers \PhpCsFixer\Diff\v2_0\Output\DiffOnlyOutputBuilder + * @covers \PhpCsFixer\Diff\v2_0\Output\UnifiedDiffOutputBuilder * - * @uses SebastianBergmann\Diff\MemoryEfficientLongestCommonSubsequenceCalculator - * @uses SebastianBergmann\Diff\TimeEfficientLongestCommonSubsequenceCalculator - * @uses SebastianBergmann\Diff\Chunk - * @uses SebastianBergmann\Diff\Diff - * @uses SebastianBergmann\Diff\Line - * @uses SebastianBergmann\Diff\Parser + * @uses \PhpCsFixer\Diff\v2_0\MemoryEfficientLongestCommonSubsequenceCalculator + * @uses \PhpCsFixer\Diff\v2_0\TimeEfficientLongestCommonSubsequenceCalculator + * @uses \PhpCsFixer\Diff\v2_0\Chunk + * @uses \PhpCsFixer\Diff\v2_0\Diff + * @uses \PhpCsFixer\Diff\v2_0\Line + * @uses \PhpCsFixer\Diff\v2_0\Parser */ final class DifferTest extends TestCase { diff --git a/tests/DifferTestTest.php b/tests/v2_0/DifferTestTest.php similarity index 98% rename from tests/DifferTestTest.php rename to tests/v2_0/DifferTestTest.php index 87ce0404..c5176031 100644 --- a/tests/DifferTestTest.php +++ b/tests/v2_0/DifferTestTest.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff; +namespace PhpCsFixer\Diff\v2_0; use PHPUnit\Framework\TestCase; diff --git a/tests/LineTest.php b/tests/v2_0/LineTest.php similarity index 92% rename from tests/LineTest.php rename to tests/v2_0/LineTest.php index 22f4e9f7..47b46a3d 100644 --- a/tests/LineTest.php +++ b/tests/v2_0/LineTest.php @@ -8,12 +8,12 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff; +namespace PhpCsFixer\Diff\v2_0; use PHPUnit\Framework\TestCase; /** - * @covers SebastianBergmann\Diff\Line + * @covers \PhpCsFixer\Diff\v2_0\Line */ final class LineTest extends TestCase { diff --git a/tests/LongestCommonSubsequenceTest.php b/tests/v2_0/LongestCommonSubsequenceTest.php similarity index 99% rename from tests/LongestCommonSubsequenceTest.php rename to tests/v2_0/LongestCommonSubsequenceTest.php index c3411e90..ef5471cb 100644 --- a/tests/LongestCommonSubsequenceTest.php +++ b/tests/v2_0/LongestCommonSubsequenceTest.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff; +namespace PhpCsFixer\Diff\v2_0; use PHPUnit\Framework\TestCase; diff --git a/tests/MemoryEfficientImplementationTest.php b/tests/v2_0/MemoryEfficientImplementationTest.php similarity index 80% rename from tests/MemoryEfficientImplementationTest.php rename to tests/v2_0/MemoryEfficientImplementationTest.php index 05ae7d7e..28f98622 100644 --- a/tests/MemoryEfficientImplementationTest.php +++ b/tests/v2_0/MemoryEfficientImplementationTest.php @@ -8,10 +8,10 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff; +namespace PhpCsFixer\Diff\v2_0; /** - * @covers SebastianBergmann\Diff\MemoryEfficientLongestCommonSubsequenceCalculator + * @covers \PhpCsFixer\Diff\v2_0\MemoryEfficientLongestCommonSubsequenceCalculator */ final class MemoryEfficientImplementationTest extends LongestCommonSubsequenceTest { diff --git a/tests/ParserTest.php b/tests/v2_0/ParserTest.php similarity index 95% rename from tests/ParserTest.php rename to tests/v2_0/ParserTest.php index 6e17496e..ad7b345f 100644 --- a/tests/ParserTest.php +++ b/tests/v2_0/ParserTest.php @@ -8,16 +8,16 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff; +namespace PhpCsFixer\Diff\v2_0; use PHPUnit\Framework\TestCase; /** - * @covers SebastianBergmann\Diff\Parser + * @covers \PhpCsFixer\Diff\v2_0\Parser * - * @uses SebastianBergmann\Diff\Chunk - * @uses SebastianBergmann\Diff\Diff - * @uses SebastianBergmann\Diff\Line + * @uses \PhpCsFixer\Diff\v2_0\Chunk + * @uses \PhpCsFixer\Diff\v2_0\Diff + * @uses \PhpCsFixer\Diff\v2_0\Line */ final class ParserTest extends TestCase { diff --git a/tests/TimeEfficientImplementationTest.php b/tests/v2_0/TimeEfficientImplementationTest.php similarity index 80% rename from tests/TimeEfficientImplementationTest.php rename to tests/v2_0/TimeEfficientImplementationTest.php index 817e6edb..16f97925 100644 --- a/tests/TimeEfficientImplementationTest.php +++ b/tests/v2_0/TimeEfficientImplementationTest.php @@ -8,10 +8,10 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff; +namespace PhpCsFixer\Diff\v2_0; /** - * @covers SebastianBergmann\Diff\TimeEfficientLongestCommonSubsequenceCalculator + * @covers \PhpCsFixer\Diff\v2_0\TimeEfficientLongestCommonSubsequenceCalculator */ final class TimeEfficientImplementationTest extends LongestCommonSubsequenceTest { diff --git a/tests/v2_0/fixtures/patch.txt b/tests/v2_0/fixtures/patch.txt new file mode 100644 index 00000000..144b61d0 --- /dev/null +++ b/tests/v2_0/fixtures/patch.txt @@ -0,0 +1,9 @@ +diff --git a/Foo.php b/Foo.php +index abcdefg..abcdefh 100644 +--- a/Foo.php ++++ b/Foo.php +@@ -20,4 +20,5 @@ class Foo + const ONE = 1; + const TWO = 2; ++ const THREE = 3; + const FOUR = 4; diff --git a/tests/v2_0/fixtures/patch2.txt b/tests/v2_0/fixtures/patch2.txt new file mode 100644 index 00000000..41fbc959 --- /dev/null +++ b/tests/v2_0/fixtures/patch2.txt @@ -0,0 +1,21 @@ +diff --git a/Foo.php b/Foo.php +index abcdefg..abcdefh 100644 +--- a/Foo.php ++++ b/Foo.php +@@ -20,4 +20,5 @@ class Foo + const ONE = 1; + const TWO = 2; ++ const THREE = 3; + const FOUR = 4; + +@@ -320,4 +320,5 @@ class Foo + const A = 'A'; + const B = 'B'; ++ const C = 'C'; + const D = 'D'; + +@@ -600,4 +600,5 @@ class Foo + public function doSomething() { + ++ return 'foo'; + } From 00695bdbffe91eb23ce55088ab57ea85075835d2 Mon Sep 17 00:00:00 2001 From: SpacePossum Date: Mon, 18 Sep 2017 16:02:13 +0200 Subject: [PATCH 05/25] Update ChangeLog.md --- ChangeLog.md | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 4f7eca66..b8767088 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,22 +1,7 @@ # ChangeLog -All notable changes are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles. +Changelog for v1.0 -## [2.0.1] - 2017-08-03 - -### Fixed - -* Fixed [#66](https://github.com/sebastianbergmann/diff/pull/66): Restored backwards compatibility for PHPUnit 6.1.4, 6.2.0, 6.2.1, 6.2.2, and 6.2.3 - -## [2.0.0] - 2017-07-11 [YANKED] - -### Added - -* Implemented [#64](https://github.com/sebastianbergmann/diff/pull/64): Show line numbers for chunks of a diff - -### Removed - -* This component is no longer supported on PHP 5.6 - -[2.0.1]: https://github.com/sebastianbergmann/diff/compare/c341c98ce083db77f896a0aa64f5ee7652915970...2.0.1 -[2.0.0]: https://github.com/sebastianbergmann/diff/compare/1.4...c341c98ce083db77f896a0aa64f5ee7652915970 +First stable release, based on: +https://github.com/sebastianbergmann/diff/releases +1.4.3 and 2.0.1 From d068edadcb8f7bc2ea3d3769cdbaf609026ec4f4 Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Sat, 23 Sep 2017 17:27:53 +0200 Subject: [PATCH 06/25] Allow to use on PHP 7.2 --- .travis.yml | 2 ++ composer.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index cc99f189..fbe6693e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,8 @@ matrix: env: COMPOSER_FLAGS="--prefer-stable" - php: 7.1 env: COMPOSER_FLAGS="--prefer-stable" + - php: 7.2 + env: COMPOSER_FLAGS="--prefer-stable" - php: nightly env: COMPOSER_FLAGS="--dev --ignore-platform-reqs" allow_failures: diff --git a/composer.json b/composer.json index a2b8dc12..49b712b3 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ } ], "require": { - "php": "^5.6 || >=7.0 <7.2" + "php": "^5.6 || ^7.0" }, "require-dev": { "phpunit/phpunit": "^4.8.35 || ^5.4.3" From 5a46d7132e51e832662cc4398fa99f302682bea4 Mon Sep 17 00:00:00 2001 From: SpacePossum Date: Thu, 5 Oct 2017 17:14:26 +0200 Subject: [PATCH 07/25] Update UnifiedDiffOutputBuilder.php see: https://github.com/sebastianbergmann/diff/commit/376cb25efae48c66fb660c9ce7bc07e1682b5e84 --- src/v2_0/Output/UnifiedDiffOutputBuilder.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/v2_0/Output/UnifiedDiffOutputBuilder.php b/src/v2_0/Output/UnifiedDiffOutputBuilder.php index 90ee2dc2..e7618651 100644 --- a/src/v2_0/Output/UnifiedDiffOutputBuilder.php +++ b/src/v2_0/Output/UnifiedDiffOutputBuilder.php @@ -114,12 +114,12 @@ private function writeChunk( if ($this->addLineNumbers) { \fwrite($output, '@@ -' . (1 + $fromStart)); - if ($fromRange > 1) { + if ($fromRange !== 1) { \fwrite($output, ',' . $fromRange); } \fwrite($output, ' +' . (1 + $toStart)); - if ($toRange > 1) { + if ($toRange !== 1) { \fwrite($output, ',' . $toRange); } From b135a0ece6c2007a74bc7e5c157b101936d054cb Mon Sep 17 00:00:00 2001 From: SpacePossum Date: Fri, 6 Oct 2017 08:20:15 +0200 Subject: [PATCH 08/25] Update DifferTest.php --- tests/v2_0/DifferTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/v2_0/DifferTest.php b/tests/v2_0/DifferTest.php index feb6cd8c..7c4b6b22 100644 --- a/tests/v2_0/DifferTest.php +++ b/tests/v2_0/DifferTest.php @@ -717,7 +717,7 @@ public function provideDiffWithLineNumbers() 'II non_patch_compat' => [ '--- Original +++ New -@@ -1,2 +1 @@ +@@ -1,2 +1,0 @@ - - ' From f0ef6133d674137e902fdf8a6f2e8e97e14a087b Mon Sep 17 00:00:00 2001 From: SpacePossum Date: Thu, 19 Oct 2017 11:10:07 +0200 Subject: [PATCH 09/25] Backport GeckoPackages/DiffOutputBuilder --- .gitignore | 1 + LICENSE | 36 +- LICENSE_DIFF | 33 ++ LICENSE_GECKO | 19 + README.md | 12 +- composer.json | 12 +- .../ConfigurationException.php | 36 ++ .../UnifiedDiffOutputBuilder.php | 295 ++++++++++++ .../Tests/AbstractDiffOutputBuilderTest.php | 97 ++++ .../Tests/ConfigurationExceptionTest.php | 32 ++ .../UnifiedDiffAssertTraitIntegrationTest.php | 135 ++++++ ...nifiedDiffOutputBuilderIntegrationTest.php | 247 +++++++++++ .../Tests/Integration/fixtures/.editorconfig | 1 + .../1_a.txt | 1 + .../1_b.txt | 0 .../2_a.txt | 35 ++ .../2_b.txt | 18 + .../Tests/Integration/out/.editorconfig | 1 + .../Tests/Integration/out/.gitignore | 2 + .../Tests/UnifiedDiffAssertTraitTest.php | 419 ++++++++++++++++++ .../UnifiedDiffOutputBuilderDataProvider.php | 197 ++++++++ .../Tests/UnifiedDiffOutputBuilderTest.php | 386 ++++++++++++++++ .../Utils/PHPUnitPolyfill.php | 49 ++ .../Utils/UnifiedDiffAssertTrait.php | 279 ++++++++++++ 24 files changed, 2305 insertions(+), 38 deletions(-) create mode 100644 LICENSE_DIFF create mode 100644 LICENSE_GECKO create mode 100644 src/GeckoPackages/DiffOutputBuilder/ConfigurationException.php create mode 100644 src/GeckoPackages/DiffOutputBuilder/UnifiedDiffOutputBuilder.php create mode 100644 tests/GeckoPackages/DiffOutputBuilder/Tests/AbstractDiffOutputBuilderTest.php create mode 100644 tests/GeckoPackages/DiffOutputBuilder/Tests/ConfigurationExceptionTest.php create mode 100644 tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/UnifiedDiffAssertTraitIntegrationTest.php create mode 100644 tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/UnifiedDiffOutputBuilderIntegrationTest.php create mode 100644 tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/fixtures/.editorconfig create mode 100644 tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_a.txt create mode 100644 tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_b.txt create mode 100644 tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_a.txt create mode 100644 tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_b.txt create mode 100644 tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/out/.editorconfig create mode 100644 tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/out/.gitignore create mode 100644 tests/GeckoPackages/DiffOutputBuilder/Tests/UnifiedDiffAssertTraitTest.php create mode 100644 tests/GeckoPackages/DiffOutputBuilder/Tests/UnifiedDiffOutputBuilderDataProvider.php create mode 100644 tests/GeckoPackages/DiffOutputBuilder/Tests/UnifiedDiffOutputBuilderTest.php create mode 100644 tests/GeckoPackages/DiffOutputBuilder/Utils/PHPUnitPolyfill.php create mode 100644 tests/GeckoPackages/DiffOutputBuilder/Utils/UnifiedDiffAssertTrait.php diff --git a/.gitignore b/.gitignore index 5cf9a2cf..bf6dd089 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /composer.lock /phpunit.xml /vendor/ +tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/out diff --git a/LICENSE b/LICENSE index e1ddf136..19a2d69c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,33 +1,5 @@ -sebastian/diff +Code from `sebastian/diff` has been forked and republished by permission of Sebastian Bergmann. +Licenced with BSD-3-Clause @ see LICENSE_DIFF, copyright (c) Sebastian Bergmann -Copyright (c) 2002-2017, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. +Code from `GeckoPackages/GeckoDiffOutputBuilder` has been copied and republished by permission of GeckoPackages. +Licenced with MIT @ see LICENSE_GECKO, copyright (c) GeckoPackages https://github.com/GeckoPackages diff --git a/LICENSE_DIFF b/LICENSE_DIFF new file mode 100644 index 00000000..e1ddf136 --- /dev/null +++ b/LICENSE_DIFF @@ -0,0 +1,33 @@ +sebastian/diff + +Copyright (c) 2002-2017, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/LICENSE_GECKO b/LICENSE_GECKO new file mode 100644 index 00000000..066294d5 --- /dev/null +++ b/LICENSE_GECKO @@ -0,0 +1,19 @@ +Copyright (c) https://github.com/GeckoPackages + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 1a2f48d4..ac448c55 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,13 @@ # PHP-CS-Fixer/diff -Fork of sebastian/diff +This is version is for PHP CS Fixer only! Do not use it! -This is version is for PHP CS Fixer only! +Code from `sebastian/diff` has been forked a republished by permission of Sebastian Bergmann. +Licenced with BSD-3-Clause @ see LICENSE_DIFF, copyright (c) Sebastian Bergmann +https://github.com/sebastianbergmann/diff -Do not use it! +Code from `GeckoPackages/GeckoDiffOutputBuilder` has been copied and republished by permission of GeckoPackages. +Licenced with MIT @ see LICENSE_GECKO, copyright (c) GeckoPackages https://github.com/GeckoPackages +https://github.com/GeckoPackages/GeckoDiffOutputBuilder/ + +For questions visit us @ https://gitter.im/PHP-CS-Fixer/Lobby diff --git a/composer.json b/composer.json index 49b712b3..f8f2377b 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "description": "sebastian/diff v2 backport support for PHP5.6", "keywords": ["diff"], "homepage": "https://github.com/PHP-CS-Fixer", - "license": "BSD-3-Clause", + "license": "BSD-3-Clause|MIT", "authors": [ { "name": "Sebastian Bergmann", @@ -12,13 +12,17 @@ { "name": "Kore Nordmann", "email": "mail@kore-nordmann.de" + }, + { + "name": "SpacePossum" } ], "require": { "php": "^5.6 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.4.3" + "phpunit/phpunit": "^4.8.35 || ^5.4.3", + "symfony/process": "^3.3" }, "autoload": { "classmap": [ @@ -28,7 +32,9 @@ "autoload-dev": { "psr-4": { "PhpCsFixer\\Diff\\v1_4\\Tests\\": "tests/v1_4", - "PhpCsFixer\\Diff\\v2_0\\Tests\\": "tests/v2_0" + "PhpCsFixer\\Diff\\v2_0\\Tests\\": "tests/v2_0", + "PhpCsFixer\\Diff\\GeckoPackages\\DiffOutputBuilder\\Tests\\": "tests/GeckoPackages/DiffOutputBuilder/Tests", + "PhpCsFixer\\Diff\\GeckoPackages\\DiffOutputBuilder\\Utils\\": "tests/GeckoPackages/DiffOutputBuilder/Utils" } } } diff --git a/src/GeckoPackages/DiffOutputBuilder/ConfigurationException.php b/src/GeckoPackages/DiffOutputBuilder/ConfigurationException.php new file mode 100644 index 00000000..315ac072 --- /dev/null +++ b/src/GeckoPackages/DiffOutputBuilder/ConfigurationException.php @@ -0,0 +1,36 @@ +' : \gettype($value).'#'.$value) + ), + $code, + $previous + ); + } +} diff --git a/src/GeckoPackages/DiffOutputBuilder/UnifiedDiffOutputBuilder.php b/src/GeckoPackages/DiffOutputBuilder/UnifiedDiffOutputBuilder.php new file mode 100644 index 00000000..8a703725 --- /dev/null +++ b/src/GeckoPackages/DiffOutputBuilder/UnifiedDiffOutputBuilder.php @@ -0,0 +1,295 @@ += 0 + */ + private $commonLineThreshold; + + /** + * @var string + */ + private $header; + + /** + * @var int >= 0 + */ + private $contextLines; + + private static $default = [ + 'contextLines' => 3, // like `diff: -u, -U NUM, --unified[=NUM]`, for patch/git apply compatibility best to keep at least @ 3 + 'collapseRanges' => true, // ranges of length one are rendered with the trailing `,1` + 'fromFile' => null, + 'fromFileDate' => null, + 'toFile' => null, + 'toFileDate' => null, + 'commonLineThreshold' => 6, // number of same lines before ending a new hunk and creating a new one (if needed) + ]; + + public function __construct(array $options = []) + { + $options = \array_merge(self::$default, $options); + + if (!\is_bool($options['collapseRanges'])) { + throw new ConfigurationException('collapseRanges', 'a bool', $options['collapseRanges']); + } + + if (!\is_int($options['contextLines']) || $options['contextLines'] < 0) { + throw new ConfigurationException('contextLines', 'an int >= 0', $options['contextLines']); + } + + if (!\is_int($options['commonLineThreshold']) || $options['commonLineThreshold'] < 1) { + throw new ConfigurationException('commonLineThreshold', 'an int > 0', $options['commonLineThreshold']); + } + + foreach (['fromFile', 'toFile'] as $option) { + if (!\is_string($options[$option])) { + throw new ConfigurationException($option, 'a string', $options[$option]); + } + } + + foreach (['fromFileDate', 'toFileDate'] as $option) { + if (null !== $options[$option] && !\is_string($options[$option])) { + throw new ConfigurationException($option, 'a string or ', $options[$option]); + } + } + + $this->header = \sprintf( + "--- %s%s\n+++ %s%s\n", + $options['fromFile'], + null === $options['fromFileDate'] ? '' : "\t".$options['fromFileDate'], + $options['toFile'], + null === $options['toFileDate'] ? '' : "\t".$options['toFileDate'] + ); + + $this->collapseRanges = $options['collapseRanges']; + $this->commonLineThreshold = $options['commonLineThreshold']; + $this->contextLines = $options['contextLines']; + } + + public function getDiff(array $diff) + { + if (0 === \count($diff)) { + return ''; + } + + $this->changed = false; + + $buffer = \fopen('php://memory', 'r+b'); + \fwrite($buffer, $this->header); + + $this->writeDiffHunks($buffer, $diff); + + $diff = \stream_get_contents($buffer, -1, 0); + + \fclose($buffer); + + if (!$this->changed) { + return ''; + } + + return $diff; + } + + private function writeDiffHunks($output, array $diff) + { + // detect "No newline at end of file" and insert into `$diff` if needed + + $upperLimit = \count($diff); + + // append "\ No newline at end of file" if needed + if (0 === $diff[$upperLimit - 1][1]) { + $lc = \substr($diff[$upperLimit - 1][0], -1); + if ("\n" !== $lc) { + \array_splice($diff, $upperLimit, 0, [["\n\\ No newline at end of file\n", self::$noNewlineAtOEFid]]); + } + } else { + // search back for the last `+` and `-` line, + // check if has trailing linebreak, else add under it warning under it + $toFind = [1 => true, 2 => true]; + for ($i = $upperLimit - 1; $i >= 0; --$i) { + if (isset($toFind[$diff[$i][1]])) { + unset($toFind[$diff[$i][1]]); + $lc = \substr($diff[$i][0], -1); + if ("\n" !== $lc) { + \array_splice($diff, $i + 1, 0, [["\n\\ No newline at end of file\n", self::$noNewlineAtOEFid]]); + } + + if (!\count($toFind)) { + break; + } + } + } + } + + // write hunks to output buffer + + $cutOff = \max($this->commonLineThreshold, $this->contextLines); + $hunkCapture = false; + $sameCount = $toRange = $fromRange = 0; + $toStart = $fromStart = 1; + + foreach ($diff as $i => $entry) { + if (0 === $entry[1]) { // same + if (false === $hunkCapture) { + ++$fromStart; + ++$toStart; + + continue; + } + + ++$sameCount; + ++$toRange; + ++$fromRange; + + if ($sameCount === $cutOff) { + $contextStartOffset = $hunkCapture - $this->contextLines < 0 + ? $hunkCapture + : $this->contextLines + ; + + $contextEndOffset = $i + $this->contextLines >= \count($diff) + ? \count($diff) - $i + : $this->contextLines + ; + + $this->writeHunk( + $diff, + $hunkCapture - $contextStartOffset, + $i - $cutOff + $contextEndOffset + 1, + $fromStart - $contextStartOffset, + $fromRange - $cutOff + $contextStartOffset + $contextEndOffset, + $toStart - $contextStartOffset, + $toRange - $cutOff + $contextStartOffset + $contextEndOffset, + $output + ); + + $fromStart += $fromRange; + $toStart += $toRange; + + $hunkCapture = false; + $sameCount = $toRange = $fromRange = 0; + } + + continue; + } + + $sameCount = 0; + + if ($entry[1] === self::$noNewlineAtOEFid) { + continue; + } + + $this->changed = true; + + if (false === $hunkCapture) { + $hunkCapture = $i; + } + + if (1 === $entry[1]) { // added + ++$toRange; + } + + if (2 === $entry[1]) { // removed + ++$fromRange; + } + } + + if (false !== $hunkCapture) { + $contextStartOffset = $hunkCapture - $this->contextLines < 0 + ? $hunkCapture + : $this->contextLines + ; + + $this->writeHunk( + $diff, + $hunkCapture - $contextStartOffset, + \count($diff), + $fromStart - $contextStartOffset, + $fromRange + $contextStartOffset, + $toStart - $contextStartOffset, + $toRange + $contextStartOffset, + $output + ); + } + } + + private function writeHunk( + array $diff, + $diffStartIndex, + $diffEndIndex, + $fromStart, + $fromRange, + $toStart, + $toRange, + $output + ) { + \fwrite($output, '@@ -'.$fromStart); + + if (!$this->collapseRanges || 1 !== $fromRange) { + \fwrite($output, ','.$fromRange); + } + + \fwrite($output, ' +'.$toStart); + if (!$this->collapseRanges || 1 !== $toRange) { + \fwrite($output, ','.$toRange); + } + + \fwrite($output, " @@\n"); + + for ($i = $diffStartIndex; $i < $diffEndIndex; ++$i) { + if ($diff[$i][1] === 1) { // added + $this->changed = true; + \fwrite($output, '+'.$diff[$i][0]); + } elseif ($diff[$i][1] === 2) { // removed + $this->changed = true; + \fwrite($output, '-'.$diff[$i][0]); + } elseif ($diff[$i][1] === 0) { // same + \fwrite($output, ' '.$diff[$i][0]); + } elseif ($diff[$i][1] === self::$noNewlineAtOEFid) { + $this->changed = true; + \fwrite($output, $diff[$i][0]); + } + } + } +} diff --git a/tests/GeckoPackages/DiffOutputBuilder/Tests/AbstractDiffOutputBuilderTest.php b/tests/GeckoPackages/DiffOutputBuilder/Tests/AbstractDiffOutputBuilderTest.php new file mode 100644 index 00000000..ad669e0c --- /dev/null +++ b/tests/GeckoPackages/DiffOutputBuilder/Tests/AbstractDiffOutputBuilderTest.php @@ -0,0 +1,97 @@ +getDiffer($options)->diff($from, $to); + + $this->assertValidDiffFormat($diff); + $this->assertSame($expected, $diff); + } + + /** + * @param string $expected + * @param string $from + * @param string $to + * @param array $options + * + * @dataProvider provideSample + */ + public function testSample($expected, $from, $to, array $options) + { + $diff = $this->getDiffer($options)->diff($from, $to); + + $this->assertValidDiffFormat($diff); + $this->assertSame($expected, $diff); + } + + /** + * Returns a new instance of a Differ with a new instance of the class (DiffOutputBuilderInterface) under test. + * + * @param array $options + * + * @return Differ + */ + protected function getDiffer(array $options = []) + { + if (null === $this->differClass) { + // map test class name (child) back to the Differ being tested. + $childClass = \get_class($this); + $differClass = 'PhpCsFixer\\Diff\\GeckoPackages\\DiffOutputBuilder\\'.\substr($childClass, \strrpos($childClass, '\\') + 1, -4); + + // basic tests: class must exist... + $this->assertTrue(\class_exists($differClass)); + + // ...and must implement DiffOutputBuilderInterface + $implements = \class_implements($differClass); + $this->assertInternalType('array', $implements); + $this->assertArrayHasKey(DiffOutputBuilderInterface::class, $implements); + + $this->differClass = $differClass; + } + + return new Differ(new $this->differClass($options)); + } +} diff --git a/tests/GeckoPackages/DiffOutputBuilder/Tests/ConfigurationExceptionTest.php b/tests/GeckoPackages/DiffOutputBuilder/Tests/ConfigurationExceptionTest.php new file mode 100644 index 00000000..1e04846b --- /dev/null +++ b/tests/GeckoPackages/DiffOutputBuilder/Tests/ConfigurationExceptionTest.php @@ -0,0 +1,32 @@ +assertSame(0, $e->getCode()); + $this->assertNull($e->getPrevious()); + $this->assertSame('Option "test" must be A, got "string#B".', $e->getMessage()); + } +} diff --git a/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/UnifiedDiffAssertTraitIntegrationTest.php b/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/UnifiedDiffAssertTraitIntegrationTest.php new file mode 100644 index 00000000..a1e7aefa --- /dev/null +++ b/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/UnifiedDiffAssertTraitIntegrationTest.php @@ -0,0 +1,135 @@ +filePatch = __DIR__.'/out/patch.txt'; + + $this->cleanUpTempFiles(); + } + + /** + * @param string $fileFrom + * @param string $fileTo + * + * @dataProvider provideFilePairsCases + */ + public function testValidPatches($fileFrom, $fileTo) + { + $command = \sprintf( + 'diff -u %s %s > %s', + \escapeshellarg(\realpath($fileFrom)), + \escapeshellarg(\realpath($fileTo)), + \escapeshellarg($this->filePatch) + ); + + $p = new Process($command); + $p->run(); + + $exitCode = $p->getExitCode(); + + if (0 === $exitCode) { + // odd case when two files have the same content. Test after executing as it is more efficient than to read the files and check the contents every time. + $this->addToAssertionCount(1); + + return; + } + + $this->assertSame( + 1, // means `diff` found a diff between the files we gave it + $exitCode, + \sprintf( + "Command exec. was not successful:\n\"%s\"\nOutput:\n\"%s\"\nStdErr:\n\"%s\"\nExit code %d.\n", + $command, + $p->getOutput(), + $p->getErrorOutput(), + $p->getExitCode() + ) + ); + + $this->assertValidUnifiedDiffFormat(\file_get_contents($this->filePatch)); + } + + /** + * @return array> + */ + public function provideFilePairsCases() + { + $cases = []; + + // created cases based on dedicated fixtures + $dir = \realpath(__DIR__.'/fixtures/UnifiedDiffAssertTraitIntegrationTest'); + $dirLength = \strlen($dir); + + for ($i = 1;; ++$i) { + $fromFile = \sprintf('%s/%d_a.txt', $dir, $i); + $toFile = \sprintf('%s/%d_b.txt', $dir, $i); + + if (!\file_exists($fromFile)) { + break; + } + + $this->assertFileExists($toFile); + $cases[\sprintf("Diff file:\n\"%s\"\nvs.\n\"%s\"\n", \substr(\realpath($fromFile), $dirLength), \substr(\realpath($toFile), $dirLength))] = [$fromFile, $toFile]; + } + + // create cases based on PHP files within the vendor directory for integration testing + $dir = \realpath(__DIR__.'/../../../../../vendor'); + $dirLength = \strlen($dir); + + $fileIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS)); + $fromFile = __FILE__; + + /** @var \SplFileInfo $file */ + foreach ($fileIterator as $file) { + if ('php' !== $file->getExtension()) { + continue; + } + + $toFile = $file->getPathname(); + $cases[\sprintf("Diff file:\n\"%s\"\nvs.\n\"%s\"\n", \substr(\realpath($fromFile), $dirLength), \substr(\realpath($toFile), $dirLength))] = [$fromFile, $toFile]; + $fromFile = $toFile; + } + + return $cases; + } + + protected function tearDown() + { + $this->cleanUpTempFiles(); + } + + private function cleanUpTempFiles() + { + @\unlink($this->filePatch); + } +} diff --git a/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/UnifiedDiffOutputBuilderIntegrationTest.php b/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/UnifiedDiffOutputBuilderIntegrationTest.php new file mode 100644 index 00000000..4fe3a89c --- /dev/null +++ b/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/UnifiedDiffOutputBuilderIntegrationTest.php @@ -0,0 +1,247 @@ +dir = __DIR__.'/out/'; + $this->fileFrom = $this->dir.'from.txt'; + $this->fileTo = $this->dir.'to.txt'; + $this->filePatch = $this->dir.'diff.patch'; + + $this->cleanUpTempFiles(); + } + + /** + * Integration test + * + * - get a file pair + * - create a `diff` between the files + * - test applying the diff using `git apply` + * - test applying the diff using `patch` + * + * @param string $fileFrom + * @param string $fileTo + * + * @dataProvider provideFilePairs + */ + public function testIntegrationUsingPHPFileInVendor($fileFrom, $fileTo) + { + $from = @\file_get_contents($fileFrom); + $this->assertInternalType('string', $from, \sprintf('Failed to read file "%s".', $fileFrom)); + + $to = @\file_get_contents($fileTo); + $this->assertInternalType('string', $to, \sprintf('Failed to read file "%s".', $fileTo)); + + $diff = (new Differ(new UnifiedDiffOutputBuilder(['fromFile' => 'Original', 'toFile' => 'New'])))->diff($from, $to); + + if ('' === $diff && $from === $to) { + // odd case: test after executing as it is more efficient than to read the files and check the contents every time + $this->addToAssertionCount(1); + + return; + } + + $this->assertNotSame('', $diff); + $this->assertValidUnifiedDiffFormat($diff); + $this->doIntegrationTest($diff, $from, $to); + } + + /** + * @param string $expected + * @param string $from + * @param string $to + * + * @dataProvider provideOutputBuildingCases + * @dataProvider provideSample + * @dataProvider provideBasicDiffGeneration + */ + public function testIntegrationOfUnitTestCases($expected, $from, $to) + { + $this->doIntegrationTest($expected, $from, $to); + } + + public function provideOutputBuildingCases() + { + return UnifiedDiffOutputBuilderDataProvider::provideOutputBuildingCases(); + } + + public function provideSample() + { + return UnifiedDiffOutputBuilderDataProvider::provideSample(); + } + + public function provideBasicDiffGeneration() + { + return UnifiedDiffOutputBuilderDataProvider::provideBasicDiffGeneration(); + } + + public function provideFilePairs() + { + $cases = []; + $fromFile = __FILE__; + $vendorDir = \realpath(__DIR__.'/../../../../../vendor'); + $vendorDirLength = \strlen($vendorDir); + $fileIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($vendorDir, \RecursiveDirectoryIterator::SKIP_DOTS)); + + /** @var \SplFileInfo $file */ + foreach ($fileIterator as $file) { + if ('php' !== $file->getExtension()) { + continue; + } + + $toFile = $file->getPathname(); + $cases[\sprintf("Diff file:\n\"%s\"\nvs.\n\"%s\"\n", \substr(\realpath($fromFile), $vendorDirLength), \substr(\realpath($toFile), $vendorDirLength))] = [$fromFile, $toFile]; + $fromFile = $toFile; + } + + return $cases; + } + + /** + * Compare diff create by builder and against one create by `diff` command. + * + * @param string $diff + * @param string $from + * @param string $to + * + * @dataProvider provideBasicDiffGeneration + */ + public function testIntegrationDiffOutputBuilderVersusDiffCommand($diff, $from, $to) + { + $this->assertNotFalse(\file_put_contents($this->fileFrom, $from)); + $this->assertNotFalse(\file_put_contents($this->fileTo, $to)); + + $p = new Process(\sprintf('diff -u %s %s', \escapeshellarg($this->fileFrom), \escapeshellarg($this->fileTo))); + $p->run(); + $this->assertSame(1, $p->getExitCode()); + + $output = $p->getOutput(); + + $diffLines = \preg_split('/(.*\R)/', $diff, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + $diffLines[0] = \preg_replace('#^\-\-\- .*#', '--- /'.$this->fileFrom, $diffLines[0], 1); + $diffLines[1] = \preg_replace('#^\+\+\+ .*#', '+++ /'.$this->fileFrom, $diffLines[1], 1); + $diff = \implode('', $diffLines); + + $outputLines = \preg_split('/(.*\R)/', $output, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + $outputLines[0] = \preg_replace('#^\-\-\- .*#', '--- /'.$this->fileFrom, $outputLines[0], 1); + $outputLines[1] = \preg_replace('#^\+\+\+ .*#', '+++ /'.$this->fileFrom, $outputLines[1], 1); + $output = \implode('', $outputLines); + + $this->assertSame($diff, $output); + } + + private function doIntegrationTest($diff, $from, $to) + { + if ('' === $diff) { + $this->addToAssertionCount(1); // Empty diff has no integration test part. + + return; + } + + $diffLines = \preg_split('/(.*\R)/', $diff, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + $diffLines[0] = \preg_replace('#^\-\-\- .*#', '--- /'.$this->fileFrom, $diffLines[0], 1); + $diffLines[1] = \preg_replace('#^\+\+\+ .*#', '+++ /'.$this->fileFrom, $diffLines[1], 1); + $diff = \implode('', $diffLines); + + $this->assertNotFalse(\file_put_contents($this->fileFrom, $from)); + $this->assertNotFalse(\file_put_contents($this->filePatch, $diff)); + + $command = \sprintf( + 'git --git-dir %s apply --check -v --unsafe-paths %s', // --unidiff-zero --ignore-whitespace + \escapeshellarg($this->dir), + \escapeshellarg($this->filePatch) + ); + + $p = new Process($command); + $p->run(); + + $this->assertTrue( + $p->isSuccessful(), + \sprintf( + "Command exec. was not successful:\n\"%s\"\nOutput:\n\"%s\"\nStdErr:\n\"%s\"\nExit code %d.\n", + $command, + $p->getOutput(), + $p->getErrorOutput(), + $p->getExitCode() + ) + ); + + $command = \sprintf( + 'patch -u --verbose --posix %s < %s', + \escapeshellarg($this->fileFrom), + \escapeshellarg($this->filePatch) + ); + + $p = new Process($command); + $p->run(); + + $output = $p->getOutput(); + + $this->assertTrue( + $p->isSuccessful(), + \sprintf( + "Command exec. was not successful:\n\"%s\"\nOutput:\n\"%s\"\nStdErr:\n\"%s\"\nExit code %d.\n", + $command, + $output, + $p->getErrorOutput(), + $p->getExitCode() + ) + ); + + $this->assertStringEqualsFile( + $this->fileFrom, $to, + \sprintf('Patch command "%s".', $command) + ); + } + + protected function tearDown() + { + $this->cleanUpTempFiles(); + } + + private function cleanUpTempFiles() + { + @\unlink($this->fileFrom.'.orig'); + @\unlink($this->fileFrom.'.rej'); + @\unlink($this->fileFrom); + @\unlink($this->fileTo); + @\unlink($this->filePatch); + } +} diff --git a/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/fixtures/.editorconfig b/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/fixtures/.editorconfig new file mode 100644 index 00000000..78b36ca0 --- /dev/null +++ b/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/fixtures/.editorconfig @@ -0,0 +1 @@ +root = true diff --git a/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_a.txt b/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_a.txt new file mode 100644 index 00000000..2e65efe2 --- /dev/null +++ b/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_a.txt @@ -0,0 +1 @@ +a \ No newline at end of file diff --git a/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_b.txt b/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_b.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_a.txt b/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_a.txt new file mode 100644 index 00000000..c7fe26e9 --- /dev/null +++ b/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_a.txt @@ -0,0 +1,35 @@ +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a \ No newline at end of file diff --git a/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_b.txt b/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_b.txt new file mode 100644 index 00000000..377a70f8 --- /dev/null +++ b/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_b.txt @@ -0,0 +1,18 @@ +a +a +a +a +a +a +a +a +a +a +b +a +a +a +a +a +a +c \ No newline at end of file diff --git a/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/out/.editorconfig b/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/out/.editorconfig new file mode 100644 index 00000000..78b36ca0 --- /dev/null +++ b/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/out/.editorconfig @@ -0,0 +1 @@ +root = true diff --git a/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/out/.gitignore b/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/out/.gitignore new file mode 100644 index 00000000..f6f7a478 --- /dev/null +++ b/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/out/.gitignore @@ -0,0 +1,2 @@ +# reset all ignore rules to create sandbox for integration test +!/** \ No newline at end of file diff --git a/tests/GeckoPackages/DiffOutputBuilder/Tests/UnifiedDiffAssertTraitTest.php b/tests/GeckoPackages/DiffOutputBuilder/Tests/UnifiedDiffAssertTraitTest.php new file mode 100644 index 00000000..764b9e2c --- /dev/null +++ b/tests/GeckoPackages/DiffOutputBuilder/Tests/UnifiedDiffAssertTraitTest.php @@ -0,0 +1,419 @@ +assertValidUnifiedDiffFormat($diff); + } + + public function provideValidCases() + { + return [ + [ +'--- Original ++++ New +@@ -8 +8 @@ +-Z ++U +', + ], + [ +'--- Original ++++ New +@@ -8 +8 @@ +-Z ++U +@@ -15 +15 @@ +-X ++V +', + ], + 'empty diff. is valid' => [ + '', + ], + ]; + } + + public function testNoLinebreakEnd() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Expected diff to end with a line break, got "C".', '#'))); + + $this->assertValidUnifiedDiffFormat("A\nB\nC"); + } + + public function testInvalidStartWithoutHeader() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote("Expected line to start with '@', '-' or '+', got \"A\n\". Line 1.", '#'))); + + $this->assertValidUnifiedDiffFormat("A\n"); + } + + public function testInvalidStartHeader1() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote("Line 1 indicates a header, so line 2 must start with \"+++\".\nLine 1: \"--- A\n\"\nLine 2: \"+ 1\n\".", '#'))); + + $this->assertValidUnifiedDiffFormat("--- A\n+ 1\n"); + } + + public function testInvalidStartHeader2() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote("Header line does not match expected pattern, got \"+++ file X\n\". Line 2.", '#'))); + + $this->assertValidUnifiedDiffFormat("--- A\n+++ file\tX\n"); + } + + public function testInvalidStartHeader3() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Date of header line does not match expected pattern, got "[invalid date]". Line 1.', '#'))); + + $this->assertValidUnifiedDiffFormat( +"--- Original\t[invalid date] ++++ New +@@ -1,2 +1,2 @@ +-A ++B + ".' +'); + } + + public function testInvalidStartHeader4() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote("Expected header line to start with \"+++ \", got \"+++INVALID\n\". Line 2.", '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++INVALID +@@ -1,2 +1,2 @@ +-A ++B + '.' +'); + } + + public function testInvalidLine1() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote("Expected line to start with '@', '-' or '+', got \"1\n\". Line 5.", '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++ New +@@ -8 +8 @@ +-Z +1 ++U +'); + } + + public function testInvalidLine2() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Expected string length of minimal 2, got 1. Line 4.', '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++ New +@@ -8 +8 @@ + + +'); + } + + public function testHunkInvalidFormat() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote("Hunk header line does not match expected pattern, got \"@@ INVALID -1,1 +1,1 @@\n\". Line 3.", '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++ New +@@ INVALID -1,1 +1,1 @@ +-Z ++U +'); + } + + public function testHunkOverlapFrom() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected new hunk; "from" (\'-\') start overlaps previous hunk. Line 6.', '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++ New +@@ -8,1 +8,1 @@ +-Z ++U +@@ -7,1 +9,1 @@ +-Z ++U +'); + } + + public function testHunkOverlapTo() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected new hunk; "to" (\'+\') start overlaps previous hunk. Line 6.', '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++ New +@@ -8,1 +8,1 @@ +-Z ++U +@@ -17,1 +7,1 @@ +-Z ++U +'); + } + + public function testExpectHunk1() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Expected hunk start (\'@\'), got "+". Line 6.', '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++ New +@@ -8 +8 @@ +-Z ++U ++O +'); + } + + public function testExpectHunk2() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected hunk start (\'@\'). Line 6.', '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++ New +@@ -8,12 +8,12 @@ + '.' + '.' +@@ -38,12 +48,12 @@ +'); + } + + public function testMisplacedLineAfterComments1() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected line as 2 "No newline" markers have found, ". Line 8.', '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++ New +@@ -8 +8 @@ +-Z +\ No newline at end of file ++U +\ No newline at end of file ++A +'); + } + + public function testMisplacedLineAfterComments2() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected line as 2 "No newline" markers have found, ". Line 7.', '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++ New +@@ -8 +8 @@ ++U +\ No newline at end of file +\ No newline at end of file +\ No newline at end of file +'); + } + + public function testMisplacedLineAfterComments3() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected line as 2 "No newline" markers have found, ". Line 7.', '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++ New +@@ -8 +8 @@ ++U +\ No newline at end of file +\ No newline at end of file ++A +'); + } + + public function testMisplacedComment() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected "\ No newline at end of file", it must be preceded by \'+\' or \'-\' line. Line 1.', '#'))); + + $this->assertValidUnifiedDiffFormat( +'\ No newline at end of file +'); + } + + public function testUnexpectedDuplicateNoNewLineEOF() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected "\\ No newline at end of file", "\\" was already closed. Line 8.', '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++ New +@@ -8,12 +8,12 @@ + '.' + '.' +\ No newline at end of file + '.' +\ No newline at end of file +'); + } + + public function testFromAfterClose() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Not expected from (\'-\'), already closed by "\ No newline at end of file". Line 6.', '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++ New +@@ -8,12 +8,12 @@ +-A +\ No newline at end of file +-A +\ No newline at end of file +'); + } + + public function testSameAfterFromClose() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Not expected same (\' \'), \'-\' already closed by "\ No newline at end of file". Line 6.', '#'))); + + $this->assertValidUnifiedDiffFormat( + '--- Original ++++ New +@@ -8,12 +8,12 @@ +-A +\ No newline at end of file + A +\ No newline at end of file +'); + } + + public function testToAfterClose() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Not expected to (\'+\'), already closed by "\ No newline at end of file". Line 6.', '#'))); + + $this->assertValidUnifiedDiffFormat( + '--- Original ++++ New +@@ -8,12 +8,12 @@ ++A +\ No newline at end of file ++A +\ No newline at end of file +'); + } + + public function testSameAfterToClose() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Not expected same (\' \'), \'+\' already closed by "\ No newline at end of file". Line 6.', '#'))); + + $this->assertValidUnifiedDiffFormat( + '--- Original ++++ New +@@ -8,12 +8,12 @@ ++A +\ No newline at end of file + A +\ No newline at end of file +'); + } + + public function testUnexpectedEOFFromMissingLines() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected EOF, number of lines in hunk "from" (\'-\')) mismatched. Line 7.', '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++ New +@@ -8,19 +7,2 @@ +-A ++B + '.' +'); + } + + public function testUnexpectedEOFToMissingLines() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected EOF, number of lines in hunk "to" (\'+\')) mismatched. Line 7.', '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++ New +@@ -8,2 +7,3 @@ +-A ++B + '.' +'); + } + + public function testUnexpectedEOFBothFromAndToMissingLines() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected EOF, number of lines in hunk "from" (\'-\')) and "to" (\'+\') mismatched. Line 7.', '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++ New +@@ -1,12 +1,14 @@ +-A ++B + '.' +'); + } +} diff --git a/tests/GeckoPackages/DiffOutputBuilder/Tests/UnifiedDiffOutputBuilderDataProvider.php b/tests/GeckoPackages/DiffOutputBuilder/Tests/UnifiedDiffOutputBuilderDataProvider.php new file mode 100644 index 00000000..b77c85c2 --- /dev/null +++ b/tests/GeckoPackages/DiffOutputBuilder/Tests/UnifiedDiffOutputBuilderDataProvider.php @@ -0,0 +1,197 @@ + 'input.txt', + 'toFile' => 'output.txt', + ], + ], + [ +'--- '.__FILE__."\t2017-10-02 17:38:11.586413675 +0100 ++++ output1.txt\t2017-10-03 12:09:43.086719482 +0100 +@@ -1,1 +1,1 @@ +-B ++X +", + "B\n", + "X\n", + [ + 'fromFile' => __FILE__, + 'fromFileDate' => '2017-10-02 17:38:11.586413675 +0100', + 'toFile' => 'output1.txt', + 'toFileDate' => '2017-10-03 12:09:43.086719482 +0100', + 'collapseRanges' => false, + ], + ], + [ +'--- input.txt ++++ output.txt +@@ -1 +1 @@ +-B ++X +', + "B\n", + "X\n", + [ + 'fromFile' => 'input.txt', + 'toFile' => 'output.txt', + 'collapseRanges' => true, + ], + ], + ]; + } + + public static function provideSample() + { + return [ + [ +'--- input.txt ++++ output.txt +@@ -1,6 +1,6 @@ + 1 + 2 + 3 +-4 ++X + 5 + 6 +', + "1\n2\n3\n4\n5\n6\n", + "1\n2\n3\nX\n5\n6\n", + [ + 'fromFile' => 'input.txt', + 'toFile' => 'output.txt', + ], + ], + ]; + } + + public static function provideBasicDiffGeneration() + { + return [ + [ +"--- input.txt ++++ output.txt +@@ -1,2 +1 @@ +-A +-B ++A\rB +", + "A\nB\n", + "A\rB\n", + ], + [ +"--- input.txt ++++ output.txt +@@ -1 +1 @@ +- ++\r +\\ No newline at end of file +", + "\n", + "\r", + ], + [ +"--- input.txt ++++ output.txt +@@ -1 +1 @@ +-\r +\\ No newline at end of file ++ +", + "\r", + "\n", + ], + [ +'--- input.txt ++++ output.txt +@@ -1,3 +1,3 @@ + X + A +-A ++B +', + "X\nA\nA\n", + "X\nA\nB\n", + ], + [ +'--- input.txt ++++ output.txt +@@ -1,3 +1,3 @@ + X + A +-A +\ No newline at end of file ++B +', + "X\nA\nA", + "X\nA\nB\n", + ], + [ +'--- input.txt ++++ output.txt +@@ -1,3 +1,3 @@ + A + A +-A ++B +\ No newline at end of file +', + "A\nA\nA\n", + "A\nA\nB", + ], + [ +'--- input.txt ++++ output.txt +@@ -1 +1 @@ +-A +\ No newline at end of file ++B +\ No newline at end of file +', + 'A', + 'B', + ], + ]; + } +} diff --git a/tests/GeckoPackages/DiffOutputBuilder/Tests/UnifiedDiffOutputBuilderTest.php b/tests/GeckoPackages/DiffOutputBuilder/Tests/UnifiedDiffOutputBuilderTest.php new file mode 100644 index 00000000..f27c69f1 --- /dev/null +++ b/tests/GeckoPackages/DiffOutputBuilder/Tests/UnifiedDiffOutputBuilderTest.php @@ -0,0 +1,386 @@ +assertValidUnifiedDiffFormat($diff); + } + + /** + * {@inheritdoc} + */ + public function provideOutputBuildingCases() + { + return UnifiedDiffOutputBuilderDataProvider::provideOutputBuildingCases(); + } + + /** + * {@inheritdoc} + */ + public function provideSample() + { + return UnifiedDiffOutputBuilderDataProvider::provideSample(); + } + + /** + * @param string $expected + * @param string $from + * @param string $to + * + * @dataProvider provideBasicDiffGeneration + */ + public function testBasicDiffGeneration($expected, $from, $to) + { + $diff = $this->getDiffer([ + 'fromFile' => 'input.txt', + 'toFile' => 'output.txt', + ])->diff($from, $to); + + $this->assertValidDiffFormat($diff); + $this->assertSame($expected, $diff); + } + + public function provideBasicDiffGeneration() + { + return UnifiedDiffOutputBuilderDataProvider::provideBasicDiffGeneration(); + } + + /** + * @param string $expected + * @param string $from + * @param string $to + * @param array $config + * + * @dataProvider provideConfiguredDiffGeneration + */ + public function testConfiguredDiffGeneration($expected, $from, $to, array $config = []) + { + $diff = $this->getDiffer(\array_merge([ + 'fromFile' => 'input.txt', + 'toFile' => 'output.txt', + ], $config))->diff($from, $to); + + $this->assertValidDiffFormat($diff); + $this->assertSame($expected, $diff); + } + + public function provideConfiguredDiffGeneration() + { + return [ + [ + '', + "1\n2", + "1\n2", + ], + [ + '', + "1\n", + "1\n", + ], + [ +'--- input.txt ++++ output.txt +@@ -4 +4 @@ +-X ++4 +', + "1\n2\n3\nX\n5\n6\n7\n8\n9\n0\n", + "1\n2\n3\n4\n5\n6\n7\n8\n9\n0\n", + [ + 'contextLines' => 0, + ], + ], + [ +'--- input.txt ++++ output.txt +@@ -3,3 +3,3 @@ + 3 +-X ++4 + 5 +', + "1\n2\n3\nX\n5\n6\n7\n8\n9\n0\n", + "1\n2\n3\n4\n5\n6\n7\n8\n9\n0\n", + [ + 'contextLines' => 1, + ], + ], + [ +'--- input.txt ++++ output.txt +@@ -1,10 +1,10 @@ + 1 + 2 + 3 +-X ++4 + 5 + 6 + 7 + 8 + 9 + 0 +', + "1\n2\n3\nX\n5\n6\n7\n8\n9\n0\n", + "1\n2\n3\n4\n5\n6\n7\n8\n9\n0\n", + [ + 'contextLines' => 999, + ], + ], + [ +'--- input.txt ++++ output.txt +@@ -1,0 +1,2 @@ ++ ++A +', + '', + "\nA\n", + ], + [ +'--- input.txt ++++ output.txt +@@ -1,2 +1,0 @@ +- +-A +', + "\nA\n", + '', + ], + [ + '--- input.txt ++++ output.txt +@@ -1,5 +1,5 @@ + 1 +-X ++2 + 3 +-Y ++4 + 5 +@@ -8,3 +8,3 @@ + 8 +-X ++9 + 0 +', + "1\nX\n3\nY\n5\n6\n7\n8\nX\n0\n", + "1\n2\n3\n4\n5\n6\n7\n8\n9\n0\n", + [ + 'commonLineThreshold' => 2, + 'contextLines' => 1, + ], + ], + [ + '--- input.txt ++++ output.txt +@@ -2 +2 @@ +-X ++2 +@@ -4 +4 @@ +-Y ++4 +@@ -9 +9 @@ +-X ++9 +', + "1\nX\n3\nY\n5\n6\n7\n8\nX\n0\n", + "1\n2\n3\n4\n5\n6\n7\n8\n9\n0\n", + [ + 'commonLineThreshold' => 1, + 'contextLines' => 0, + ], + ], + ]; + } + + public function testReUseBuilder() + { + $differ = $this->getDiffer([ + 'fromFile' => 'input.txt', + 'toFile' => 'output.txt', + ]); + + $diff = $differ->diff("A\nB\n", "A\nX\n"); + $this->assertSame( +'--- input.txt ++++ output.txt +@@ -1,2 +1,2 @@ + A +-B ++X +', + $diff + ); + + $diff = $differ->diff("A\n", "A\n"); + $this->assertSame( + '', + $diff + ); + } + + public function testEmptyDiff() + { + $builder = new UnifiedDiffOutputBuilder([ + 'fromFile' => 'input.txt', + 'toFile' => 'output.txt', + ]); + + $this->assertSame( + '', + $builder->getDiff([]) + ); + } + + /** + * @param array $options + * @param string $message + * + * @dataProvider provideInvalidConfiguration + */ + public function testInvalidConfiguration(array $options, $message) + { + $this->expectException(ConfigurationException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote($message, '#'))); + + new UnifiedDiffOutputBuilder($options); + } + + public function provideInvalidConfiguration() + { + $time = \time(); + + return [ + [ + ['collapseRanges' => 1], + 'Option "collapseRanges" must be a bool, got "integer#1".', + ], + [ + ['contextLines' => 'a'], + 'Option "contextLines" must be an int >= 0, got "string#a".', + ], + [ + ['commonLineThreshold' => -2], + 'Option "commonLineThreshold" must be an int > 0, got "integer#-2".', + ], + [ + ['commonLineThreshold' => 0], + 'Option "commonLineThreshold" must be an int > 0, got "integer#0".', + ], + [ + ['fromFile' => new \SplFileInfo(__FILE__)], + 'Option "fromFile" must be a string, got "SplFileInfo".', + ], + [ + ['fromFile' => null], + 'Option "fromFile" must be a string, got "".', + ], + [ + [ + 'fromFile' => __FILE__, + 'toFile' => 1, + ], + 'Option "toFile" must be a string, got "integer#1".', + ], + [ + [ + 'fromFile' => __FILE__, + 'toFile' => __FILE__, + 'toFileDate' => $time, + ], + 'Option "toFileDate" must be a string or , got "integer#'.$time.'".', + ], + [ + [], + 'Option "fromFile" must be a string, got "".', + ], + ]; + } + + /** + * @param string $expected + * @param string $from + * @param string $to + * @param int $threshold + * + * @dataProvider provideCommonLineThresholdCases + */ + public function testCommonLineThreshold($expected, $from, $to, $threshold) + { + $diff = $this->getDiffer([ + 'fromFile' => 'input.txt', + 'toFile' => 'output.txt', + 'commonLineThreshold' => $threshold, + 'contextLines' => 0, + ])->diff($from, $to); + + $this->assertValidDiffFormat($diff); + $this->assertSame($expected, $diff); + } + + public function provideCommonLineThresholdCases() + { + return [ + [ +'--- input.txt ++++ output.txt +@@ -2,3 +2,3 @@ +-X ++B + C12 +-Y ++D +@@ -7 +7 @@ +-X ++Z +', + "A\nX\nC12\nY\nA\nA\nX\n", + "A\nB\nC12\nD\nA\nA\nZ\n", + 2, + ], + [ +'--- input.txt ++++ output.txt +@@ -2 +2 @@ +-X ++B +@@ -4 +4 @@ +-Y ++D +', + "A\nX\nV\nY\n", + "A\nB\nV\nD\n", + 1, + ], + ]; + } +} diff --git a/tests/GeckoPackages/DiffOutputBuilder/Utils/PHPUnitPolyfill.php b/tests/GeckoPackages/DiffOutputBuilder/Utils/PHPUnitPolyfill.php new file mode 100644 index 00000000..8e03414e --- /dev/null +++ b/tests/GeckoPackages/DiffOutputBuilder/Utils/PHPUnitPolyfill.php @@ -0,0 +1,49 @@ +wellYeahShipIt('expectedException', $exception); + } + + public function expectExceptionMessageRegExp($messageRegExp) + { + if (\method_exists(TestCase::class, 'expectExceptionMessageRegExp')) { + return parent::expectExceptionMessageRegExp($messageRegExp); + } + + $this->wellYeahShipIt('expectedExceptionMessageRegExp', $messageRegExp); + } + + private function wellYeahShipIt($key, $value) + { + $self = new \ReflectionClass(PHPUnit_Framework_TestCase::class); + $property = $self->getProperty($key); + $property->setAccessible(true); + $property->setValue($this, $value); + } +} diff --git a/tests/GeckoPackages/DiffOutputBuilder/Utils/UnifiedDiffAssertTrait.php b/tests/GeckoPackages/DiffOutputBuilder/Utils/UnifiedDiffAssertTrait.php new file mode 100644 index 00000000..0a5d1e6f --- /dev/null +++ b/tests/GeckoPackages/DiffOutputBuilder/Utils/UnifiedDiffAssertTrait.php @@ -0,0 +1,279 @@ +addToAssertionCount(1); + + return; + } + + // test diff ends with a line break + $last = \substr($diff, -1); + if ("\n" !== $last && "\r" !== $last) { + throw new \UnexpectedValueException(\sprintf('Expected diff to end with a line break, got "%s".', $last)); + } + + $lines = \preg_split('/(.*\R)/', $diff, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + $lineCount = \count($lines); + $lineNumber = $diffLineFromNumber = $diffLineToNumber = 1; + $fromStart = $fromTillOffset = $toStart = $toTillOffset = -1; + $expectHunkHeader = true; + + // check for header + if ($lineCount > 1) { + $this->unifiedDiffAssertLinePrefix($lines[0], 'Line 1.'); + $this->unifiedDiffAssertLinePrefix($lines[1], 'Line 2.'); + + if ('---' === \substr($lines[0], 0, 3)) { + if ('+++' !== \substr($lines[1], 0, 3)) { + throw new \UnexpectedValueException(\sprintf("Line 1 indicates a header, so line 2 must start with \"+++\".\nLine 1: \"%s\"\nLine 2: \"%s\".", $lines[0], $lines[1])); + } + + $this->unifiedDiffAssertHeaderLine($lines[0], '--- ', 'Line 1.'); + $this->unifiedDiffAssertHeaderLine($lines[1], '+++ ', 'Line 2.'); + + $lineNumber = 3; + } + } + + $endOfLineTypes = []; + $diffClosed = false; + + // assert format of lines, get all hunks, test the line numbers + for (; $lineNumber <= $lineCount; ++$lineNumber) { + if ($diffClosed) { + throw new \UnexpectedValueException(\sprintf('Unexpected line as 2 "No newline" markers have found, ". Line %d.', $lineNumber)); + } + + $line = $lines[$lineNumber - 1]; // line numbers start by 1, array index at 0 + $type = $this->unifiedDiffAssertLinePrefix($line, \sprintf('Line %d.', $lineNumber)); + + if ($expectHunkHeader && '@' !== $type && '\\' !== $type) { + throw new \UnexpectedValueException(\sprintf('Expected hunk start (\'@\'), got "%s". Line %d.', $type, $lineNumber)); + } + + if ('@' === $type) { + if (!$expectHunkHeader) { + throw new \UnexpectedValueException(\sprintf('Unexpected hunk start (\'@\'). Line %d.', $lineNumber)); + } + + $previousHunkFromEnd = $fromStart + $fromTillOffset; + $previousHunkTillEnd = $toStart + $toTillOffset; + + list($fromStart, $fromTillOffset, $toStart, $toTillOffset) = $this->unifiedDiffAssertHunkHeader($line, \sprintf('Line %d.', $lineNumber)); + + // detect overlapping hunks + if ($fromStart < $previousHunkFromEnd) { + throw new \UnexpectedValueException(\sprintf('Unexpected new hunk; "from" (\'-\') start overlaps previous hunk. Line %d.', $lineNumber)); + } + + if ($toStart < $previousHunkTillEnd) { + throw new \UnexpectedValueException(\sprintf('Unexpected new hunk; "to" (\'+\') start overlaps previous hunk. Line %d.', $lineNumber)); + } + + /* valid states; hunks touches against each other: + $fromStart === $previousHunkFromEnd + $toStart === $previousHunkTillEnd + */ + + $diffLineFromNumber = $fromStart; + $diffLineToNumber = $toStart; + $expectHunkHeader = false; + + continue; + } + + if ('-' === $type) { + if (isset($endOfLineTypes['-'])) { + throw new \UnexpectedValueException(\sprintf('Not expected from (\'-\'), already closed by "\\ No newline at end of file". Line %d.', $lineNumber)); + } + + ++$diffLineFromNumber; + } elseif ('+' === $type) { + if (isset($endOfLineTypes['+'])) { + throw new \UnexpectedValueException(\sprintf('Not expected to (\'+\'), already closed by "\\ No newline at end of file". Line %d.', $lineNumber)); + } + + ++$diffLineToNumber; + } elseif (' ' === $type) { + if (isset($endOfLineTypes['-'])) { + throw new \UnexpectedValueException(\sprintf('Not expected same (\' \'), \'-\' already closed by "\\ No newline at end of file". Line %d.', $lineNumber)); + } + + if (isset($endOfLineTypes['+'])) { + throw new \UnexpectedValueException(\sprintf('Not expected same (\' \'), \'+\' already closed by "\\ No newline at end of file". Line %d.', $lineNumber)); + } + + ++$diffLineFromNumber; + ++$diffLineToNumber; + } elseif ('\\' === $type) { + if (!isset($lines[$lineNumber - 2])) { + throw new \UnexpectedValueException(\sprintf('Unexpected "\\ No newline at end of file", it must be preceded by \'+\' or \'-\' line. Line %d.', $lineNumber)); + } + + $previousType = $this->unifiedDiffAssertLinePrefix($lines[$lineNumber - 2], \sprintf('Preceding line of "\\ No newline at end of file" of unexpected format. Line %d.', $lineNumber)); + if (isset($endOfLineTypes[$previousType])) { + throw new \UnexpectedValueException(\sprintf('Unexpected "\\ No newline at end of file", "%s" was already closed. Line %d.', $type, $lineNumber)); + } + + $endOfLineTypes[$previousType] = true; + $diffClosed = \count($endOfLineTypes) > 1; + } else { + // internal state error + throw new \RuntimeException(\sprintf('Unexpected line type "%s" Line %d.', $type, $lineNumber)); + } + + $expectHunkHeader = + $diffLineFromNumber === ($fromStart + $fromTillOffset) + && $diffLineToNumber === ($toStart + $toTillOffset); + } + + if ( + $diffLineFromNumber !== ($fromStart + $fromTillOffset) + && $diffLineToNumber !== ($toStart + $toTillOffset) + ) { + throw new \UnexpectedValueException(\sprintf('Unexpected EOF, number of lines in hunk "from" (\'-\')) and "to" (\'+\') mismatched. Line %d.', $lineNumber)); + } + + if ($diffLineFromNumber !== ($fromStart + $fromTillOffset)) { + throw new \UnexpectedValueException(\sprintf('Unexpected EOF, number of lines in hunk "from" (\'-\')) mismatched. Line %d.', $lineNumber)); + } + + if ($diffLineToNumber !== ($toStart + $toTillOffset)) { + throw new \UnexpectedValueException(\sprintf('Unexpected EOF, number of lines in hunk "to" (\'+\')) mismatched. Line %d.', $lineNumber)); + } + + $this->addToAssertionCount(1); + } + + /** + * @param string $line + * @param string $message + * + * @return string '+', '-', '@', ' ' or '\' + */ + private function unifiedDiffAssertLinePrefix($line, $message) + { + $this->unifiedDiffAssertStrLength($line, 2, $message); // 2: line type indicator ('+', '-', ' ' or '\') and a line break + $firstChar = $line[0]; + + if ('+' === $firstChar || '-' === $firstChar || '@' === $firstChar || ' ' === $firstChar) { + return $firstChar; + } + + if ("\\ No newline at end of file\n" === $line) { + return '\\'; + } + + throw new \UnexpectedValueException(\sprintf('Expected line to start with \'@\', \'-\' or \'+\', got "%s". %s', $line, $message)); + } + + private function unifiedDiffAssertStrLength($line, $min, $message) + { + $length = \strlen($line); + if ($length < $min) { + throw new \UnexpectedValueException(\sprintf('Expected string length of minimal %d, got %d. %s', $min, $length, $message)); + } + } + + /** + * Assert valid unified diff header line + * + * Samples: + * - "+++ from1.txt\t2017-08-24 19:51:29.383985722 +0200" + * - "+++ from1.txt" + * + * @param string $line + * @param string $start + * @param string $message + */ + private function unifiedDiffAssertHeaderLine($line, $start, $message) + { + if (0 !== \strpos($line, $start)) { + throw new \UnexpectedValueException(\sprintf('Expected header line to start with "%s", got "%s". %s', $start.' ', $line, $message)); + } + + // sample "+++ from1.txt\t2017-08-24 19:51:29.383985722 +0200\n" + $match = \preg_match( + "/^([^\t]*)(?:[\t]([\\S].*[\\S]))?\n$/", + \substr($line, 4), // 4 === string length of "+++ " / "--- " + $matches + ); + + if (1 !== $match) { + throw new \UnexpectedValueException(\sprintf('Header line does not match expected pattern, got "%s". %s', $line, $message)); + } + + // $file = $matches[1]; + + if (\count($matches) > 2) { + $this->unifiedDiffAssertHeaderDate($matches[2], $message); + } + } + + private function unifiedDiffAssertHeaderDate($date, $message) + { + // sample "2017-08-24 19:51:29.383985722 +0200" + $match = \preg_match( + '/^([\d]{4})-([01]?[\d])-([0123]?[\d])(:? [\d]{1,2}:[\d]{1,2}(?::[\d]{1,2}(:?\.[\d]+)?)?(?: ([\+\-][\d]{4}))?)?$/', + $date, + $matches + ); + + if (1 !== $match || ($matchesCount = \count($matches)) < 4) { + throw new \UnexpectedValueException(\sprintf('Date of header line does not match expected pattern, got "%s". %s', $date, $message)); + } + + // [$full, $year, $month, $day, $time] = $matches; + } + + /** + * @param string $line + * @param string $message + * + * @return int[] + */ + private function unifiedDiffAssertHunkHeader($line, $message) + { + if (1 !== \preg_match('#^@@ -([\d]+)((?:,[\d]+)?) \+([\d]+)((?:,[\d]+)?) @@\n$#', $line, $matches)) { + throw new \UnexpectedValueException( + \sprintf( + 'Hunk header line does not match expected pattern, got "%s". %s', + $line, + $message + ) + ); + } + + return [ + (int) $matches[1], + empty($matches[2]) ? 1 : (int) \substr($matches[2], 1), + (int) $matches[3], + empty($matches[4]) ? 1 : (int) \substr($matches[4], 1), + ]; + } +} From c247cb06b60d5d461c8313f3e0d3cef2d035ad95 Mon Sep 17 00:00:00 2001 From: SpacePossum Date: Thu, 19 Oct 2017 13:08:28 +0200 Subject: [PATCH 10/25] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index f8f2377b..ceffd95d 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "description": "sebastian/diff v2 backport support for PHP5.6", "keywords": ["diff"], "homepage": "https://github.com/PHP-CS-Fixer", - "license": "BSD-3-Clause|MIT", + "license": "BSD-3-Clause", "authors": [ { "name": "Sebastian Bergmann", From 6ef825b8587d2c71a195c83c43626547e61b1ee2 Mon Sep 17 00:00:00 2001 From: SpacePossum Date: Sat, 21 Oct 2017 12:27:55 +0200 Subject: [PATCH 11/25] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ac448c55..d4106257 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # PHP-CS-Fixer/diff -This is version is for PHP CS Fixer only! Do not use it! +This version is for PHP CS Fixer only! Do not use it! Code from `sebastian/diff` has been forked a republished by permission of Sebastian Bergmann. Licenced with BSD-3-Clause @ see LICENSE_DIFF, copyright (c) Sebastian Bergmann From b95b8c02c58670b15612cfc60873f3f7f5290484 Mon Sep 17 00:00:00 2001 From: SpacePossum Date: Sat, 21 Oct 2017 12:28:17 +0200 Subject: [PATCH 12/25] Update LICENSE_DIFF --- LICENSE_DIFF | 2 -- 1 file changed, 2 deletions(-) diff --git a/LICENSE_DIFF b/LICENSE_DIFF index e1ddf136..93cf0088 100644 --- a/LICENSE_DIFF +++ b/LICENSE_DIFF @@ -1,5 +1,3 @@ -sebastian/diff - Copyright (c) 2002-2017, Sebastian Bergmann . All rights reserved. From 78bb099e9c16361126c86ce82ec4405ebab8e756 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Rumi=C5=84ski?= Date: Thu, 15 Feb 2018 17:58:55 +0100 Subject: [PATCH 13/25] Add Diff v3 backport (#7) * add v3 "as-is" --- composer.json | 3 +- src/v1_4/Differ.php | 2 +- src/v2_0/Differ.php | 2 +- src/v3_0/Chunk.php | 78 ++ src/v3_0/Diff.php | 67 ++ src/v3_0/Differ.php | 329 +++++++++ src/v3_0/Exception/ConfigurationException.php | 40 + src/v3_0/Exception/Exception.php | 15 + .../Exception/InvalidArgumentException.php | 15 + src/v3_0/Line.php | 44 ++ .../LongestCommonSubsequenceCalculator.php | 24 + ...ientLongestCommonSubsequenceCalculator.php | 81 +++ .../Output/AbstractChunkOutputBuilder.php | 56 ++ src/v3_0/Output/DiffOnlyOutputBuilder.php | 66 ++ .../Output/DiffOutputBuilderInterface.php | 20 + .../Output/StrictUnifiedDiffOutputBuilder.php | 315 ++++++++ src/v3_0/Output/UnifiedDiffOutputBuilder.php | 259 +++++++ src/v3_0/Parser.php | 106 +++ ...ientLongestCommonSubsequenceCalculator.php | 66 ++ tests/v1_4/DifferTest.php | 2 +- tests/v2_0/DifferTest.php | 12 +- tests/v3_0/ChunkTest.php | 68 ++ tests/v3_0/DiffTest.php | 55 ++ tests/v3_0/DifferTest.php | 462 ++++++++++++ .../Exception/ConfigurationExceptionTest.php | 41 ++ .../InvalidArgumentExceptionTest.php | 33 + tests/v3_0/LineTest.php | 44 ++ tests/v3_0/LongestCommonSubsequenceTest.php | 201 +++++ .../MemoryEfficientImplementationTest.php | 22 + .../Output/AbstractChunkOutputBuilderTest.php | 154 ++++ .../v3_0/Output/DiffOnlyOutputBuilderTest.php | 76 ++ ...nifiedDiffOutputBuilderIntegrationTest.php | 299 ++++++++ ...nifiedDiffOutputBuilderIntegrationTest.php | 163 +++++ ...ctUnifiedDiffOutputBuilderDataProvider.php | 189 +++++ .../StrictUnifiedDiffOutputBuilderTest.php | 684 ++++++++++++++++++ .../UnifiedDiffOutputBuilderDataProvider.php | 396 ++++++++++ .../Output/UnifiedDiffOutputBuilderTest.php | 90 +++ tests/v3_0/ParserTest.php | 152 ++++ .../v3_0/TimeEfficientImplementationTest.php | 22 + tests/v3_0/Utils/FileUtils.php | 30 + tests/v3_0/Utils/UnifiedDiffAssertTrait.php | 274 +++++++ .../UnifiedDiffAssertTraitIntegrationTest.php | 129 ++++ .../v3_0/Utils/UnifiedDiffAssertTraitTest.php | 434 +++++++++++ tests/v3_0/fixtures/.editorconfig | 1 + .../1_a.txt | 1 + .../1_b.txt | 0 .../2_a.txt | 35 + .../2_b.txt | 18 + tests/v3_0/fixtures/out/.editorconfig | 1 + tests/v3_0/fixtures/out/.gitignore | 2 + tests/v3_0/fixtures/patch.txt | 9 + tests/v3_0/fixtures/patch2.txt | 21 + 52 files changed, 5698 insertions(+), 10 deletions(-) create mode 100644 src/v3_0/Chunk.php create mode 100644 src/v3_0/Diff.php create mode 100644 src/v3_0/Differ.php create mode 100644 src/v3_0/Exception/ConfigurationException.php create mode 100644 src/v3_0/Exception/Exception.php create mode 100644 src/v3_0/Exception/InvalidArgumentException.php create mode 100644 src/v3_0/Line.php create mode 100644 src/v3_0/LongestCommonSubsequenceCalculator.php create mode 100644 src/v3_0/MemoryEfficientLongestCommonSubsequenceCalculator.php create mode 100644 src/v3_0/Output/AbstractChunkOutputBuilder.php create mode 100644 src/v3_0/Output/DiffOnlyOutputBuilder.php create mode 100644 src/v3_0/Output/DiffOutputBuilderInterface.php create mode 100644 src/v3_0/Output/StrictUnifiedDiffOutputBuilder.php create mode 100644 src/v3_0/Output/UnifiedDiffOutputBuilder.php create mode 100644 src/v3_0/Parser.php create mode 100644 src/v3_0/TimeEfficientLongestCommonSubsequenceCalculator.php create mode 100644 tests/v3_0/ChunkTest.php create mode 100644 tests/v3_0/DiffTest.php create mode 100644 tests/v3_0/DifferTest.php create mode 100644 tests/v3_0/Exception/ConfigurationExceptionTest.php create mode 100644 tests/v3_0/Exception/InvalidArgumentExceptionTest.php create mode 100644 tests/v3_0/LineTest.php create mode 100644 tests/v3_0/LongestCommonSubsequenceTest.php create mode 100644 tests/v3_0/MemoryEfficientImplementationTest.php create mode 100644 tests/v3_0/Output/AbstractChunkOutputBuilderTest.php create mode 100644 tests/v3_0/Output/DiffOnlyOutputBuilderTest.php create mode 100644 tests/v3_0/Output/Integration/StrictUnifiedDiffOutputBuilderIntegrationTest.php create mode 100644 tests/v3_0/Output/Integration/UnifiedDiffOutputBuilderIntegrationTest.php create mode 100644 tests/v3_0/Output/StrictUnifiedDiffOutputBuilderDataProvider.php create mode 100644 tests/v3_0/Output/StrictUnifiedDiffOutputBuilderTest.php create mode 100644 tests/v3_0/Output/UnifiedDiffOutputBuilderDataProvider.php create mode 100644 tests/v3_0/Output/UnifiedDiffOutputBuilderTest.php create mode 100644 tests/v3_0/ParserTest.php create mode 100644 tests/v3_0/TimeEfficientImplementationTest.php create mode 100644 tests/v3_0/Utils/FileUtils.php create mode 100644 tests/v3_0/Utils/UnifiedDiffAssertTrait.php create mode 100644 tests/v3_0/Utils/UnifiedDiffAssertTraitIntegrationTest.php create mode 100644 tests/v3_0/Utils/UnifiedDiffAssertTraitTest.php create mode 100644 tests/v3_0/fixtures/.editorconfig create mode 100644 tests/v3_0/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_a.txt create mode 100644 tests/v3_0/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_b.txt create mode 100644 tests/v3_0/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_a.txt create mode 100644 tests/v3_0/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_b.txt create mode 100644 tests/v3_0/fixtures/out/.editorconfig create mode 100644 tests/v3_0/fixtures/out/.gitignore create mode 100644 tests/v3_0/fixtures/patch.txt create mode 100644 tests/v3_0/fixtures/patch2.txt diff --git a/composer.json b/composer.json index ceffd95d..e08be75b 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ "php": "^5.6 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.4.3", + "phpunit/phpunit": "^5.7.23 || ^6.4.3", "symfony/process": "^3.3" }, "autoload": { @@ -33,6 +33,7 @@ "psr-4": { "PhpCsFixer\\Diff\\v1_4\\Tests\\": "tests/v1_4", "PhpCsFixer\\Diff\\v2_0\\Tests\\": "tests/v2_0", + "PhpCsFixer\\Diff\\v3_0\\": "tests/v3_0", "PhpCsFixer\\Diff\\GeckoPackages\\DiffOutputBuilder\\Tests\\": "tests/GeckoPackages/DiffOutputBuilder/Tests", "PhpCsFixer\\Diff\\GeckoPackages\\DiffOutputBuilder\\Utils\\": "tests/GeckoPackages/DiffOutputBuilder/Utils" } diff --git a/src/v1_4/Differ.php b/src/v1_4/Differ.php index a6ef45e2..b2015abf 100644 --- a/src/v1_4/Differ.php +++ b/src/v1_4/Differ.php @@ -232,7 +232,7 @@ public function diffToArray($from, $to, LongestCommonSubsequence $lcs = null) if ($this->detectUnmatchedLineEndings($fromMatches, $toMatches)) { $diff[] = array( - '#Warning: Strings contain different line endings!', + '#Warnings contain different line endings!', 0 ); } diff --git a/src/v2_0/Differ.php b/src/v2_0/Differ.php index ed66d867..a8f7dba7 100644 --- a/src/v2_0/Differ.php +++ b/src/v2_0/Differ.php @@ -158,7 +158,7 @@ public function diffToArray($from, $to, LongestCommonSubsequenceCalculator $lcs } if ($this->detectUnmatchedLineEndings($diff)) { - \array_unshift($diff, ["#Warning: Strings contain different line endings!\n", 3]); + \array_unshift($diff, ["#Warnings contain different line endings!\n", 3]); } return $diff; diff --git a/src/v3_0/Chunk.php b/src/v3_0/Chunk.php new file mode 100644 index 00000000..6b633c17 --- /dev/null +++ b/src/v3_0/Chunk.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0; + +final class Chunk +{ + /** + * @var int + */ + private $start; + + /** + * @var int + */ + private $startRange; + + /** + * @var int + */ + private $end; + + /** + * @var int + */ + private $endRange; + + /** + * @var array + */ + private $lines; + + public function __construct($start = 0, $startRange = 1, $end = 0, $endRange = 1, array $lines = []) + { + $this->start = $start; + $this->startRange = $startRange; + $this->end = $end; + $this->endRange = $endRange; + $this->lines = $lines; + } + + public function getStart() + { + return $this->start; + } + + public function getStartRange() + { + return $this->startRange; + } + + public function getEnd() + { + return $this->end; + } + + public function getEndRange() + { + return $this->endRange; + } + + public function getLines() + { + return $this->lines; + } + + public function setLines(array $lines) + { + $this->lines = $lines; + } +} diff --git a/src/v3_0/Diff.php b/src/v3_0/Diff.php new file mode 100644 index 00000000..9f537ec9 --- /dev/null +++ b/src/v3_0/Diff.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0; + +final class Diff +{ + /** + * @var string + */ + private $from; + + /** + * @var string + */ + private $to; + + /** + * @var Chunk[] + */ + private $chunks; + + /** + * @param string $from + * @param string $to + * @param Chunk[] $chunks + */ + public function __construct($from, $to, array $chunks = []) + { + $this->from = $from; + $this->to = $to; + $this->chunks = $chunks; + } + + public function getFrom() + { + return $this->from; + } + + public function getTo() + { + return $this->to; + } + + /** + * @return Chunk[] + */ + public function getChunks() + { + return $this->chunks; + } + + /** + * @param Chunk[] $chunks + */ + public function setChunks(array $chunks) + { + $this->chunks = $chunks; + } +} diff --git a/src/v3_0/Differ.php b/src/v3_0/Differ.php new file mode 100644 index 00000000..42315f5a --- /dev/null +++ b/src/v3_0/Differ.php @@ -0,0 +1,329 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0; + +use PhpCsFixer\Diff\v3_0\Output\DiffOutputBuilderInterface; +use PhpCsFixer\Diff\v3_0\Output\UnifiedDiffOutputBuilder; + +/** + * Diff implementation. + */ +final class Differ +{ + const OLD = 0; + const ADDED = 1; + const REMOVED = 2; + const DIFF_LINE_END_WARNING = 3; + const NO_LINE_END_EOF_WARNING = 4; + + /** + * @var DiffOutputBuilderInterface + */ + private $outputBuilder; + + /** + * @param DiffOutputBuilderInterface $outputBuilder + * + * @throws InvalidArgumentException + */ + public function __construct($outputBuilder = null) + { + if ($outputBuilder instanceof DiffOutputBuilderInterface) { + $this->outputBuilder = $outputBuilder; + } elseif (null === $outputBuilder) { + $this->outputBuilder = new UnifiedDiffOutputBuilder; + } elseif (\is_string($outputBuilder)) { + // PHPUnit 6.1.4, 6.2.0, 6.2.1, 6.2.2, and 6.2.3 support + // @see https://github.com/sebastianbergmann/phpunit/issues/2734#issuecomment-314514056 + // @deprecated + $this->outputBuilder = new UnifiedDiffOutputBuilder($outputBuilder); + } else { + throw new InvalidArgumentException( + \sprintf( + 'Expected builder to be an instance of DiffOutputBuilderInterface, or a string, got %s.', + \is_object($outputBuilder) ? 'instance of "' . \get_class($outputBuilder) . '"' : \gettype($outputBuilder) . ' "' . $outputBuilder . '"' + ) + ); + } + } + + /** + * Returns the diff between two arrays or strings as string. + * + * @param array|string $from + * @param array|string $to + * @param null|LongestCommonSubsequenceCalculator $lcs + * + * @return string + */ + public function diff($from, $to, LongestCommonSubsequenceCalculator $lcs = null) + { + $diff = $this->diffToArray( + $this->normalizeDiffInput($from), + $this->normalizeDiffInput($to), + $lcs + ); + + return $this->outputBuilder->getDiff($diff); + } + + /** + * Returns the diff between two arrays or strings as array. + * + * Each array element contains two elements: + * - [0] => mixed $token + * - [1] => 2|1|0 + * + * - 2: REMOVED: $token was removed from $from + * - 1: ADDED: $token was added to $from + * - 0: OLD: $token is not changed in $to + * + * @param array|string $from + * @param array|string $to + * @param LongestCommonSubsequenceCalculator $lcs + * + * @return array + */ + public function diffToArray($from, $to, LongestCommonSubsequenceCalculator $lcs = null) + { + if (\is_string($from)) { + $from = $this->splitStringByLines($from); + } elseif (!\is_array($from)) { + throw new InvalidArgumentException('"from" must be an array or string.'); + } + + if (\is_string($to)) { + $to = $this->splitStringByLines($to); + } elseif (!\is_array($to)) { + throw new InvalidArgumentException('"to" must be an array or string.'); + } + + list($from, $to, $start, $end) = self::getArrayDiffParted($from, $to); + + if ($lcs === null) { + $lcs = $this->selectLcsImplementation($from, $to); + } + + $common = $lcs->calculate(\array_values($from), \array_values($to)); + $diff = []; + + foreach ($start as $token) { + $diff[] = [$token, self::OLD]; + } + + \reset($from); + \reset($to); + + foreach ($common as $token) { + while (($fromToken = \reset($from)) !== $token) { + $diff[] = [\array_shift($from), self::REMOVED]; + } + + while (($toToken = \reset($to)) !== $token) { + $diff[] = [\array_shift($to), self::ADDED]; + } + + $diff[] = [$token, self::OLD]; + + \array_shift($from); + \array_shift($to); + } + + while (($token = \array_shift($from)) !== null) { + $diff[] = [$token, self::REMOVED]; + } + + while (($token = \array_shift($to)) !== null) { + $diff[] = [$token, self::ADDED]; + } + + foreach ($end as $token) { + $diff[] = [$token, self::OLD]; + } + + if ($this->detectUnmatchedLineEndings($diff)) { + \array_unshift($diff, ["#Warnings contain different line endings!\n", self::DIFF_LINE_END_WARNING]); + } + + return $diff; + } + + /** + * Casts variable to string if it is not a string or array. + * + * @param mixed $input + * + * @return array|string + */ + private function normalizeDiffInput($input) + { + if (!\is_array($input) && !\is_string($input)) { + return (string) $input; + } + + return $input; + } + + /** + * Checks if input is string, if so it will split it line-by-line. + * + * @param string $input + * + * @return array + */ + private function splitStringByLines($input) + { + return \preg_split('/(.*\R)/', $input, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + } + + /** + * @param array $from + * @param array $to + * + * @return LongestCommonSubsequenceCalculator + */ + private function selectLcsImplementation(array $from, array $to) + { + // We do not want to use the time-efficient implementation if its memory + // footprint will probably exceed this value. Note that the footprint + // calculation is only an estimation for the matrix and the LCS method + // will typically allocate a bit more memory than this. + $memoryLimit = 100 * 1024 * 1024; + + if ($this->calculateEstimatedFootprint($from, $to) > $memoryLimit) { + return new MemoryEfficientLongestCommonSubsequenceCalculator; + } + + return new TimeEfficientLongestCommonSubsequenceCalculator; + } + + /** + * Calculates the estimated memory footprint for the DP-based method. + * + * @param array $from + * @param array $to + * + * @return float|int + */ + private function calculateEstimatedFootprint(array $from, array $to) + { + $itemSize = PHP_INT_SIZE === 4 ? 76 : 144; + + return $itemSize * \min(\count($from), \count($to)) ** 2; + } + + /** + * Returns true if line ends don't match in a diff. + * + * @param array $diff + * + * @return bool + */ + private function detectUnmatchedLineEndings(array $diff) + { + $newLineBreaks = ['' => true]; + $oldLineBreaks = ['' => true]; + + foreach ($diff as $entry) { + if (self::OLD === $entry[1]) { + $ln = $this->getLinebreak($entry[0]); + $oldLineBreaks[$ln] = true; + $newLineBreaks[$ln] = true; + } elseif (self::ADDED === $entry[1]) { + $newLineBreaks[$this->getLinebreak($entry[0])] = true; + } elseif (self::REMOVED === $entry[1]) { + $oldLineBreaks[$this->getLinebreak($entry[0])] = true; + } + } + + // if either input or output is a single line without breaks than no warning should be raised + if (['' => true] === $newLineBreaks || ['' => true] === $oldLineBreaks) { + return false; + } + + // two way compare + foreach ($newLineBreaks as $break => $set) { + if (!isset($oldLineBreaks[$break])) { + return true; + } + } + + foreach ($oldLineBreaks as $break => $set) { + if (!isset($newLineBreaks[$break])) { + return true; + } + } + + return false; + } + + private function getLinebreak($line) + { + if (!\is_string($line)) { + return ''; + } + + $lc = \substr($line, -1); + if ("\r" === $lc) { + return "\r"; + } + + if ("\n" !== $lc) { + return ''; + } + + if ("\r\n" === \substr($line, -2)) { + return "\r\n"; + } + + return "\n"; + } + + private static function getArrayDiffParted(array &$from, array &$to) + { + $start = []; + $end = []; + + \reset($to); + + foreach ($from as $k => $v) { + $toK = \key($to); + + if ($toK === $k && $v === $to[$k]) { + $start[$k] = $v; + + unset($from[$k], $to[$k]); + } else { + break; + } + } + + \end($from); + \end($to); + + do { + $fromK = \key($from); + $toK = \key($to); + + if (null === $fromK || null === $toK || \current($from) !== \current($to)) { + break; + } + + \prev($from); + \prev($to); + + $end = [$fromK => $from[$fromK]] + $end; + unset($from[$fromK], $to[$toK]); + } while (true); + + return [$from, $to, $start, $end]; + } +} diff --git a/src/v3_0/Exception/ConfigurationException.php b/src/v3_0/Exception/ConfigurationException.php new file mode 100644 index 00000000..8892d8f9 --- /dev/null +++ b/src/v3_0/Exception/ConfigurationException.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0; + +final class ConfigurationException extends InvalidArgumentException +{ + /** + * @param string $option + * @param string $expected + * @param mixed $value + * @param int $code + * @param null|\Exception $previous + */ + public function __construct( + $option, + $expected, + $value, + $code = 0, + \Exception $previous = null + ) { + parent::__construct( + \sprintf( + 'Option "%s" must be %s, got "%s".', + $option, + $expected, + \is_object($value) ? \get_class($value) : (null === $value ? '' : \gettype($value) . '#' . $value) + ), + $code, + $previous + ); + } +} diff --git a/src/v3_0/Exception/Exception.php b/src/v3_0/Exception/Exception.php new file mode 100644 index 00000000..7a391db7 --- /dev/null +++ b/src/v3_0/Exception/Exception.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0; + +interface Exception +{ +} diff --git a/src/v3_0/Exception/InvalidArgumentException.php b/src/v3_0/Exception/InvalidArgumentException.php new file mode 100644 index 00000000..50854824 --- /dev/null +++ b/src/v3_0/Exception/InvalidArgumentException.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0; + +class InvalidArgumentException extends \InvalidArgumentException implements Exception +{ +} diff --git a/src/v3_0/Line.php b/src/v3_0/Line.php new file mode 100644 index 00000000..07be8805 --- /dev/null +++ b/src/v3_0/Line.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0; + +final class Line +{ + const ADDED = 1; + const REMOVED = 2; + const UNCHANGED = 3; + + /** + * @var int + */ + private $type; + + /** + * @var string + */ + private $content; + + public function __construct($type = self::UNCHANGED, $content = '') + { + $this->type = $type; + $this->content = $content; + } + + public function getContent() + { + return $this->content; + } + + public function getType() + { + return $this->type; + } +} diff --git a/src/v3_0/LongestCommonSubsequenceCalculator.php b/src/v3_0/LongestCommonSubsequenceCalculator.php new file mode 100644 index 00000000..be5ed5a1 --- /dev/null +++ b/src/v3_0/LongestCommonSubsequenceCalculator.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0; + +interface LongestCommonSubsequenceCalculator +{ + /** + * Calculates the longest common subsequence of two arrays. + * + * @param array $from + * @param array $to + * + * @return array + */ + public function calculate(array $from, array $to); +} diff --git a/src/v3_0/MemoryEfficientLongestCommonSubsequenceCalculator.php b/src/v3_0/MemoryEfficientLongestCommonSubsequenceCalculator.php new file mode 100644 index 00000000..85a1c4e6 --- /dev/null +++ b/src/v3_0/MemoryEfficientLongestCommonSubsequenceCalculator.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0; + +final class MemoryEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator +{ + /** + * {@inheritdoc} + */ + public function calculate(array $from, array $to) + { + $cFrom = \count($from); + $cTo = \count($to); + + if ($cFrom === 0) { + return []; + } + + if ($cFrom === 1) { + if (\in_array($from[0], $to, true)) { + return [$from[0]]; + } + + return []; + } + + $i = (int) ($cFrom / 2); + $fromStart = \array_slice($from, 0, $i); + $fromEnd = \array_slice($from, $i); + $llB = $this->length($fromStart, $to); + $llE = $this->length(\array_reverse($fromEnd), \array_reverse($to)); + $jMax = 0; + $max = 0; + + for ($j = 0; $j <= $cTo; $j++) { + $m = $llB[$j] + $llE[$cTo - $j]; + + if ($m >= $max) { + $max = $m; + $jMax = $j; + } + } + + $toStart = \array_slice($to, 0, $jMax); + $toEnd = \array_slice($to, $jMax); + + return \array_merge( + $this->calculate($fromStart, $toStart), + $this->calculate($fromEnd, $toEnd) + ); + } + + private function length(array $from, array $to) + { + $current = \array_fill(0, \count($to) + 1, 0); + $cFrom = \count($from); + $cTo = \count($to); + + for ($i = 0; $i < $cFrom; $i++) { + $prev = $current; + + for ($j = 0; $j < $cTo; $j++) { + if ($from[$i] === $to[$j]) { + $current[$j + 1] = $prev[$j] + 1; + } else { + $current[$j + 1] = \max($current[$j], $prev[$j + 1]); + } + } + } + + return $current; + } +} diff --git a/src/v3_0/Output/AbstractChunkOutputBuilder.php b/src/v3_0/Output/AbstractChunkOutputBuilder.php new file mode 100644 index 00000000..ecf7d321 --- /dev/null +++ b/src/v3_0/Output/AbstractChunkOutputBuilder.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0\Output; + +abstract class AbstractChunkOutputBuilder implements DiffOutputBuilderInterface +{ + /** + * Takes input of the diff array and returns the common parts. + * Iterates through diff line by line. + * + * @param array $diff + * @param int $lineThreshold + * + * @return array + */ + protected function getCommonChunks(array $diff, $lineThreshold = 5) + { + $diffSize = \count($diff); + $capturing = false; + $chunkStart = 0; + $chunkSize = 0; + $commonChunks = []; + + for ($i = 0; $i < $diffSize; ++$i) { + if ($diff[$i][1] === 0 /* OLD */) { + if ($capturing === false) { + $capturing = true; + $chunkStart = $i; + $chunkSize = 0; + } else { + ++$chunkSize; + } + } elseif ($capturing !== false) { + if ($chunkSize >= $lineThreshold) { + $commonChunks[$chunkStart] = $chunkStart + $chunkSize; + } + + $capturing = false; + } + } + + if ($capturing !== false && $chunkSize >= $lineThreshold) { + $commonChunks[$chunkStart] = $chunkStart + $chunkSize; + } + + return $commonChunks; + } +} diff --git a/src/v3_0/Output/DiffOnlyOutputBuilder.php b/src/v3_0/Output/DiffOnlyOutputBuilder.php new file mode 100644 index 00000000..0f3b81f5 --- /dev/null +++ b/src/v3_0/Output/DiffOnlyOutputBuilder.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0\Output; + +use PhpCsFixer\Diff\v3_0\Differ; + +/** + * Builds a diff string representation in a loose unified diff format + * listing only changes lines. Does not include line numbers. + */ +final class DiffOnlyOutputBuilder implements DiffOutputBuilderInterface +{ + /** + * @var string + */ + private $header; + + public function __construct($header = "--- Original\n+++ New\n") + { + $this->header = $header; + } + + public function getDiff(array $diff) + { + $buffer = \fopen('php://memory', 'r+b'); + + if ('' !== $this->header) { + \fwrite($buffer, $this->header); + if ("\n" !== \substr($this->header, -1, 1)) { + \fwrite($buffer, "\n"); + } + } + + foreach ($diff as $diffEntry) { + if ($diffEntry[1] === Differ::ADDED) { + \fwrite($buffer, '+' . $diffEntry[0]); + } elseif ($diffEntry[1] === Differ::REMOVED) { + \fwrite($buffer, '-' . $diffEntry[0]); + } elseif ($diffEntry[1] === Differ::DIFF_LINE_END_WARNING) { + \fwrite($buffer, ' ' . $diffEntry[0]); + + continue; // Warnings should not be tested for line break, it will always be there + } else { /* Not changed (old) 0 */ + continue; // we didn't write the non changs line, so do not add a line break either + } + + $lc = \substr($diffEntry[0], -1); + if ($lc !== "\n" && $lc !== "\r") { + \fwrite($buffer, "\n"); // \No newline at end of file + } + } + + $diff = \stream_get_contents($buffer, -1, 0); + \fclose($buffer); + + return $diff; + } +} diff --git a/src/v3_0/Output/DiffOutputBuilderInterface.php b/src/v3_0/Output/DiffOutputBuilderInterface.php new file mode 100644 index 00000000..ae690e6a --- /dev/null +++ b/src/v3_0/Output/DiffOutputBuilderInterface.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0\Output; + +/** + * Defines how an output builder should take a generated + * diff array and return a string representation of that diff. + */ +interface DiffOutputBuilderInterface +{ + public function getDiff(array $diff); +} diff --git a/src/v3_0/Output/StrictUnifiedDiffOutputBuilder.php b/src/v3_0/Output/StrictUnifiedDiffOutputBuilder.php new file mode 100644 index 00000000..49faa8ad --- /dev/null +++ b/src/v3_0/Output/StrictUnifiedDiffOutputBuilder.php @@ -0,0 +1,315 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0\Output; + +use PhpCsFixer\Diff\v3_0\ConfigurationException; +use PhpCsFixer\Diff\v3_0\Differ; + +/** + * Strict Unified diff output builder. + * + * Generates (strict) Unified diff's (unidiffs) with hunks. + */ +final class StrictUnifiedDiffOutputBuilder implements DiffOutputBuilderInterface +{ + /** + * @var bool + */ + private $changed; + + /** + * @var bool + */ + private $collapseRanges; + + /** + * @var int >= 0 + */ + private $commonLineThreshold; + + /** + * @var string + */ + private $header; + + /** + * @var int >= 0 + */ + private $contextLines; + + private static $default = [ + 'collapseRanges' => true, // ranges of length one are rendered with the trailing `,1` + 'commonLineThreshold' => 6, // number of same lines before ending a new hunk and creating a new one (if needed) + 'contextLines' => 3, // like `diff: -u, -U NUM, --unified[=NUM]`, for patch/git apply compatibility best to keep at least @ 3 + 'fromFile' => null, + 'fromFileDate' => null, + 'toFile' => null, + 'toFileDate' => null, + ]; + + public function __construct(array $options = []) + { + $options = \array_merge(self::$default, $options); + + if (!\is_bool($options['collapseRanges'])) { + throw new ConfigurationException('collapseRanges', 'a bool', $options['collapseRanges']); + } + + if (!\is_int($options['contextLines']) || $options['contextLines'] < 0) { + throw new ConfigurationException('contextLines', 'an int >= 0', $options['contextLines']); + } + + if (!\is_int($options['commonLineThreshold']) || $options['commonLineThreshold'] <= 0) { + throw new ConfigurationException('commonLineThreshold', 'an int > 0', $options['commonLineThreshold']); + } + + foreach (['fromFile', 'toFile'] as $option) { + if (!\is_string($options[$option])) { + throw new ConfigurationException($option, 'a string', $options[$option]); + } + } + + foreach (['fromFileDate', 'toFileDate'] as $option) { + if (null !== $options[$option] && !\is_string($options[$option])) { + throw new ConfigurationException($option, 'a string or ', $options[$option]); + } + } + + $this->header = \sprintf( + "--- %s%s\n+++ %s%s\n", + $options['fromFile'], + null === $options['fromFileDate'] ? '' : "\t" . $options['fromFileDate'], + $options['toFile'], + null === $options['toFileDate'] ? '' : "\t" . $options['toFileDate'] + ); + + $this->collapseRanges = $options['collapseRanges']; + $this->commonLineThreshold = $options['commonLineThreshold']; + $this->contextLines = $options['contextLines']; + } + + public function getDiff(array $diff) + { + if (0 === \count($diff)) { + return ''; + } + + $this->changed = false; + + $buffer = \fopen('php://memory', 'r+b'); + \fwrite($buffer, $this->header); + + $this->writeDiffHunks($buffer, $diff); + + if (!$this->changed) { + \fclose($buffer); + + return ''; + } + + $diff = \stream_get_contents($buffer, -1, 0); + + \fclose($buffer); + + // If the last char is not a linebreak: add it. + // This might happen when both the `from` and `to` do not have a trailing linebreak + $last = \substr($diff, -1); + + return "\n" !== $last && "\r" !== $last + ? $diff . "\n" + : $diff + ; + } + + private function writeDiffHunks($output, array $diff) + { + // detect "No newline at end of file" and insert into `$diff` if needed + + $upperLimit = \count($diff); + + if (0 === $diff[$upperLimit - 1][1]) { + $lc = \substr($diff[$upperLimit - 1][0], -1); + if ("\n" !== $lc) { + \array_splice($diff, $upperLimit, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); + } + } else { + // search back for the last `+` and `-` line, + // check if has trailing linebreak, else add under it warning under it + $toFind = [1 => true, 2 => true]; + for ($i = $upperLimit - 1; $i >= 0; --$i) { + if (isset($toFind[$diff[$i][1]])) { + unset($toFind[$diff[$i][1]]); + $lc = \substr($diff[$i][0], -1); + if ("\n" !== $lc) { + \array_splice($diff, $i + 1, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); + } + + if (!\count($toFind)) { + break; + } + } + } + } + + // write hunks to output buffer + + $cutOff = \max($this->commonLineThreshold, $this->contextLines); + $hunkCapture = false; + $sameCount = $toRange = $fromRange = 0; + $toStart = $fromStart = 1; + + foreach ($diff as $i => $entry) { + if (0 === $entry[1]) { // same + if (false === $hunkCapture) { + ++$fromStart; + ++$toStart; + + continue; + } + + ++$sameCount; + ++$toRange; + ++$fromRange; + + if ($sameCount === $cutOff) { + $contextStartOffset = ($hunkCapture - $this->contextLines) < 0 + ? $hunkCapture + : $this->contextLines + ; + + // note: $contextEndOffset = $this->contextLines; + // + // because we never go beyond the end of the diff. + // with the cutoff/contextlines here the follow is never true; + // + // if ($i - $cutOff + $this->contextLines + 1 > \count($diff)) { + // $contextEndOffset = count($diff) - 1; + // } + // + // ; that would be true for a trailing incomplete hunk case which is dealt with after this loop + + $this->writeHunk( + $diff, + $hunkCapture - $contextStartOffset, + $i - $cutOff + $this->contextLines + 1, + $fromStart - $contextStartOffset, + $fromRange - $cutOff + $contextStartOffset + $this->contextLines, + $toStart - $contextStartOffset, + $toRange - $cutOff + $contextStartOffset + $this->contextLines, + $output + ); + + $fromStart += $fromRange; + $toStart += $toRange; + + $hunkCapture = false; + $sameCount = $toRange = $fromRange = 0; + } + + continue; + } + + $sameCount = 0; + + if ($entry[1] === Differ::NO_LINE_END_EOF_WARNING) { + continue; + } + + $this->changed = true; + + if (false === $hunkCapture) { + $hunkCapture = $i; + } + + if (Differ::ADDED === $entry[1]) { // added + ++$toRange; + } + + if (Differ::REMOVED === $entry[1]) { // removed + ++$fromRange; + } + } + + if (false === $hunkCapture) { + return; + } + + // we end here when cutoff (commonLineThreshold) was not reached, but we where capturing a hunk, + // do not render hunk till end automatically because the number of context lines might be less than the commonLineThreshold + + $contextStartOffset = $hunkCapture - $this->contextLines < 0 + ? $hunkCapture + : $this->contextLines + ; + + // prevent trying to write out more common lines than there are in the diff _and_ + // do not write more than configured through the context lines + $contextEndOffset = \min($sameCount, $this->contextLines); + + $fromRange -= $sameCount; + $toRange -= $sameCount; + + $this->writeHunk( + $diff, + $hunkCapture - $contextStartOffset, + $i - $sameCount + $contextEndOffset + 1, + $fromStart - $contextStartOffset, + $fromRange + $contextStartOffset + $contextEndOffset, + $toStart - $contextStartOffset, + $toRange + $contextStartOffset + $contextEndOffset, + $output + ); + } + + private function writeHunk( + array $diff, + $diffStartIndex, + $diffEndIndex, + $fromStart, + $fromRange, + $toStart, + $toRange, + $output + ) { + \fwrite($output, '@@ -' . $fromStart); + + if (!$this->collapseRanges || 1 !== $fromRange) { + \fwrite($output, ',' . $fromRange); + } + + \fwrite($output, ' +' . $toStart); + if (!$this->collapseRanges || 1 !== $toRange) { + \fwrite($output, ',' . $toRange); + } + + \fwrite($output, " @@\n"); + + for ($i = $diffStartIndex; $i < $diffEndIndex; ++$i) { + if ($diff[$i][1] === Differ::ADDED) { + $this->changed = true; + \fwrite($output, '+' . $diff[$i][0]); + } elseif ($diff[$i][1] === Differ::REMOVED) { + $this->changed = true; + \fwrite($output, '-' . $diff[$i][0]); + } elseif ($diff[$i][1] === Differ::OLD) { + \fwrite($output, ' ' . $diff[$i][0]); + } elseif ($diff[$i][1] === Differ::NO_LINE_END_EOF_WARNING) { + $this->changed = true; + \fwrite($output, $diff[$i][0]); + } + //} elseif ($diff[$i][1] === Differ::DIFF_LINE_END_WARNING) { // custom comment inserted by PHPUnit/diff package + // skip + //} else { + // unknown/invalid + //} + } + } +} diff --git a/src/v3_0/Output/UnifiedDiffOutputBuilder.php b/src/v3_0/Output/UnifiedDiffOutputBuilder.php new file mode 100644 index 00000000..5e7276ab --- /dev/null +++ b/src/v3_0/Output/UnifiedDiffOutputBuilder.php @@ -0,0 +1,259 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0\Output; + +use PhpCsFixer\Diff\v3_0\Differ; + +/** + * Builds a diff string representation in unified diff format in chunks. + */ +final class UnifiedDiffOutputBuilder extends AbstractChunkOutputBuilder +{ + /** + * @var bool + */ + private $collapseRanges = true; + + /** + * @var int >= 0 + */ + private $commonLineThreshold = 6; + + /** + * @var int >= 0 + */ + private $contextLines = 3; + + /** + * @var string + */ + private $header; + + /** + * @var bool + */ + private $addLineNumbers; + + public function __construct($header = "--- Original\n+++ New\n", $addLineNumbers = false) + { + $this->header = $header; + $this->addLineNumbers = $addLineNumbers; + } + + public function getDiff(array $diff) + { + $buffer = \fopen('php://memory', 'r+b'); + + if ('' !== $this->header) { + \fwrite($buffer, $this->header); + if ("\n" !== \substr($this->header, -1, 1)) { + \fwrite($buffer, "\n"); + } + } + + if (0 !== \count($diff)) { + $this->writeDiffHunks($buffer, $diff); + } + + $diff = \stream_get_contents($buffer, -1, 0); + + \fclose($buffer); + + // If the last char is not a linebreak: add it. + // This might happen when both the `from` and `to` do not have a trailing linebreak + $last = \substr($diff, -1); + + return "\n" !== $last && "\r" !== $last + ? $diff . "\n" + : $diff + ; + } + + private function writeDiffHunks($output, array $diff) + { + // detect "No newline at end of file" and insert into `$diff` if needed + + $upperLimit = \count($diff); + + if (0 === $diff[$upperLimit - 1][1]) { + $lc = \substr($diff[$upperLimit - 1][0], -1); + if ("\n" !== $lc) { + \array_splice($diff, $upperLimit, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); + } + } else { + // search back for the last `+` and `-` line, + // check if has trailing linebreak, else add under it warning under it + $toFind = [1 => true, 2 => true]; + for ($i = $upperLimit - 1; $i >= 0; --$i) { + if (isset($toFind[$diff[$i][1]])) { + unset($toFind[$diff[$i][1]]); + $lc = \substr($diff[$i][0], -1); + if ("\n" !== $lc) { + \array_splice($diff, $i + 1, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); + } + + if (!\count($toFind)) { + break; + } + } + } + } + + // write hunks to output buffer + + $cutOff = \max($this->commonLineThreshold, $this->contextLines); + $hunkCapture = false; + $sameCount = $toRange = $fromRange = 0; + $toStart = $fromStart = 1; + + foreach ($diff as $i => $entry) { + if (0 === $entry[1]) { // same + if (false === $hunkCapture) { + ++$fromStart; + ++$toStart; + + continue; + } + + ++$sameCount; + ++$toRange; + ++$fromRange; + + if ($sameCount === $cutOff) { + $contextStartOffset = ($hunkCapture - $this->contextLines) < 0 + ? $hunkCapture + : $this->contextLines + ; + + // note: $contextEndOffset = $this->contextLines; + // + // because we never go beyond the end of the diff. + // with the cutoff/contextlines here the follow is never true; + // + // if ($i - $cutOff + $this->contextLines + 1 > \count($diff)) { + // $contextEndOffset = count($diff) - 1; + // } + // + // ; that would be true for a trailing incomplete hunk case which is dealt with after this loop + + $this->writeHunk( + $diff, + $hunkCapture - $contextStartOffset, + $i - $cutOff + $this->contextLines + 1, + $fromStart - $contextStartOffset, + $fromRange - $cutOff + $contextStartOffset + $this->contextLines, + $toStart - $contextStartOffset, + $toRange - $cutOff + $contextStartOffset + $this->contextLines, + $output + ); + + $fromStart += $fromRange; + $toStart += $toRange; + + $hunkCapture = false; + $sameCount = $toRange = $fromRange = 0; + } + + continue; + } + + $sameCount = 0; + + if ($entry[1] === Differ::NO_LINE_END_EOF_WARNING) { + continue; + } + + if (false === $hunkCapture) { + $hunkCapture = $i; + } + + if (Differ::ADDED === $entry[1]) { + ++$toRange; + } + + if (Differ::REMOVED === $entry[1]) { + ++$fromRange; + } + } + + if (false === $hunkCapture) { + return; + } + + // we end here when cutoff (commonLineThreshold) was not reached, but we where capturing a hunk, + // do not render hunk till end automatically because the number of context lines might be less than the commonLineThreshold + + $contextStartOffset = $hunkCapture - $this->contextLines < 0 + ? $hunkCapture + : $this->contextLines + ; + + // prevent trying to write out more common lines than there are in the diff _and_ + // do not write more than configured through the context lines + $contextEndOffset = \min($sameCount, $this->contextLines); + + $fromRange -= $sameCount; + $toRange -= $sameCount; + + $this->writeHunk( + $diff, + $hunkCapture - $contextStartOffset, + $i - $sameCount + $contextEndOffset + 1, + $fromStart - $contextStartOffset, + $fromRange + $contextStartOffset + $contextEndOffset, + $toStart - $contextStartOffset, + $toRange + $contextStartOffset + $contextEndOffset, + $output + ); + } + + private function writeHunk( + array $diff, + $diffStartIndex, + $diffEndIndex, + $fromStart, + $fromRange, + $toStart, + $toRange, + $output + ) { + if ($this->addLineNumbers) { + \fwrite($output, '@@ -' . $fromStart); + + if (!$this->collapseRanges || 1 !== $fromRange) { + \fwrite($output, ',' . $fromRange); + } + + \fwrite($output, ' +' . $toStart); + if (!$this->collapseRanges || 1 !== $toRange) { + \fwrite($output, ',' . $toRange); + } + + \fwrite($output, " @@\n"); + } else { + \fwrite($output, "@@ @@\n"); + } + + for ($i = $diffStartIndex; $i < $diffEndIndex; ++$i) { + if ($diff[$i][1] === Differ::ADDED) { + \fwrite($output, '+' . $diff[$i][0]); + } elseif ($diff[$i][1] === Differ::REMOVED) { + \fwrite($output, '-' . $diff[$i][0]); + } elseif ($diff[$i][1] === Differ::OLD) { + \fwrite($output, ' ' . $diff[$i][0]); + } elseif ($diff[$i][1] === Differ::NO_LINE_END_EOF_WARNING) { + \fwrite($output, "\n"); // $diff[$i][0] + } else { /* Not changed (old) Differ::OLD or Warning Differ::DIFF_LINE_END_WARNING */ + \fwrite($output, ' ' . $diff[$i][0]); + } + } + } +} diff --git a/src/v3_0/Parser.php b/src/v3_0/Parser.php new file mode 100644 index 00000000..aa7175a3 --- /dev/null +++ b/src/v3_0/Parser.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0; + +/** + * Unified diff parser. + */ +final class Parser +{ + /** + * @param string $string + * + * @return Diff[] + */ + public function parse($string) + { + $lines = \preg_split('(\r\n|\r|\n)', $string); + + if (!empty($lines) && $lines[\count($lines) - 1] === '') { + \array_pop($lines); + } + + $lineCount = \count($lines); + $diffs = []; + $diff = null; + $collected = []; + + for ($i = 0; $i < $lineCount; ++$i) { + if (\preg_match('(^---\\s+(?P\\S+))', $lines[$i], $fromMatch) && + \preg_match('(^\\+\\+\\+\\s+(?P\\S+))', $lines[$i + 1], $toMatch)) { + if ($diff !== null) { + $this->parseFileDiff($diff, $collected); + + $diffs[] = $diff; + $collected = []; + } + + $diff = new Diff($fromMatch['file'], $toMatch['file']); + + ++$i; + } else { + if (\preg_match('/^(?:diff --git |index [\da-f\.]+|[+-]{3} [ab])/', $lines[$i])) { + continue; + } + + $collected[] = $lines[$i]; + } + } + + if ($diff !== null && \count($collected)) { + $this->parseFileDiff($diff, $collected); + + $diffs[] = $diff; + } + + return $diffs; + } + + private function parseFileDiff(Diff $diff, array $lines) + { + $chunks = []; + $chunk = null; + + foreach ($lines as $line) { + if (\preg_match('/^@@\s+-(?P\d+)(?:,\s*(?P\d+))?\s+\+(?P\d+)(?:,\s*(?P\d+))?\s+@@/', $line, $match)) { + $chunk = new Chunk( + (int) $match['start'], + isset($match['startrange']) ? \max(1, (int) $match['startrange']) : 1, + (int) $match['end'], + isset($match['endrange']) ? \max(1, (int) $match['endrange']) : 1 + ); + + $chunks[] = $chunk; + $diffLines = []; + + continue; + } + + if (\preg_match('/^(?P[+ -])?(?P.*)/', $line, $match)) { + $type = Line::UNCHANGED; + + if ($match['type'] === '+') { + $type = Line::ADDED; + } elseif ($match['type'] === '-') { + $type = Line::REMOVED; + } + + $diffLines[] = new Line($type, $match['line']); + + if (null !== $chunk) { + $chunk->setLines($diffLines); + } + } + } + + $diff->setChunks($chunks); + } +} diff --git a/src/v3_0/TimeEfficientLongestCommonSubsequenceCalculator.php b/src/v3_0/TimeEfficientLongestCommonSubsequenceCalculator.php new file mode 100644 index 00000000..644968ac --- /dev/null +++ b/src/v3_0/TimeEfficientLongestCommonSubsequenceCalculator.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0; + +final class TimeEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator +{ + /** + * {@inheritdoc} + */ + public function calculate(array $from, array $to) + { + $common = []; + $fromLength = \count($from); + $toLength = \count($to); + $width = $fromLength + 1; + $matrix = new \SplFixedArray($width * ($toLength + 1)); + + for ($i = 0; $i <= $fromLength; ++$i) { + $matrix[$i] = 0; + } + + for ($j = 0; $j <= $toLength; ++$j) { + $matrix[$j * $width] = 0; + } + + for ($i = 1; $i <= $fromLength; ++$i) { + for ($j = 1; $j <= $toLength; ++$j) { + $o = ($j * $width) + $i; + $matrix[$o] = \max( + $matrix[$o - 1], + $matrix[$o - $width], + $from[$i - 1] === $to[$j - 1] ? $matrix[$o - $width - 1] + 1 : 0 + ); + } + } + + $i = $fromLength; + $j = $toLength; + + while ($i > 0 && $j > 0) { + if ($from[$i - 1] === $to[$j - 1]) { + $common[] = $from[$i - 1]; + --$i; + --$j; + } else { + $o = ($j * $width) + $i; + + if ($matrix[$o - $width] > $matrix[$o - 1]) { + --$j; + } else { + --$i; + } + } + } + + return \array_reverse($common); + } +} diff --git a/tests/v1_4/DifferTest.php b/tests/v1_4/DifferTest.php index 6f9c41bd..3c382a77 100644 --- a/tests/v1_4/DifferTest.php +++ b/tests/v1_4/DifferTest.php @@ -227,7 +227,7 @@ public function arrayProvider() 'test line diff detection' => array( array( array( - '#Warning: Strings contain different line endings!', + '#Warnings contain different line endings!', self::OLD, ), array( diff --git a/tests/v2_0/DifferTest.php b/tests/v2_0/DifferTest.php index 7c4b6b22..0283897f 100644 --- a/tests/v2_0/DifferTest.php +++ b/tests/v2_0/DifferTest.php @@ -269,7 +269,7 @@ public function arrayProvider() 'test line diff detection' => [ [ [ - "#Warning: Strings contain different line endings!\n", + "#Warnings contain different line endings!\n", self::WARNING, ], [ @@ -287,7 +287,7 @@ public function arrayProvider() 'test line diff detection in array input' => [ [ [ - "#Warning: Strings contain different line endings!\n", + "#Warnings contain different line endings!\n", self::WARNING, ], [ @@ -384,12 +384,12 @@ public function textProvider() "B\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1", ], [ - "--- Original\n+++ New\n@@ @@\n #Warning: Strings contain different line endings!\n- [ - "--- Original\n+++ New\n@@ -1 +1 @@\n #Warning: Strings contain different line endings!\n- + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0; + +use PHPUnit\Framework\TestCase; + +/** + * @covers PhpCsFixer\Diff\v3_0\Chunk + */ +final class ChunkTest extends TestCase +{ + /** + * @var Chunk + */ + private $chunk; + + protected function setUp() + { + $this->chunk = new Chunk; + } + + public function testCanBeCreatedWithoutArguments() + { + $this->assertInstanceOf(Chunk::class, $this->chunk); + } + + public function testStartCanBeRetrieved() + { + $this->assertSame(0, $this->chunk->getStart()); + } + + public function testStartRangeCanBeRetrieved() + { + $this->assertSame(1, $this->chunk->getStartRange()); + } + + public function testEndCanBeRetrieved() + { + $this->assertSame(0, $this->chunk->getEnd()); + } + + public function testEndRangeCanBeRetrieved() + { + $this->assertSame(1, $this->chunk->getEndRange()); + } + + public function testLinesCanBeRetrieved() + { + $this->assertSame([], $this->chunk->getLines()); + } + + public function testLinesCanBeSet() + { + $this->assertSame([], $this->chunk->getLines()); + + $testValue = ['line0', 'line1']; + $this->chunk->setLines($testValue); + $this->assertSame($testValue, $this->chunk->getLines()); + } +} diff --git a/tests/v3_0/DiffTest.php b/tests/v3_0/DiffTest.php new file mode 100644 index 00000000..7cc658e1 --- /dev/null +++ b/tests/v3_0/DiffTest.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0; + +use PHPUnit\Framework\TestCase; + +/** + * @covers PhpCsFixer\Diff\v3_0\Diff + * + * @uses PhpCsFixer\Diff\v3_0\Chunk + */ +final class DiffTest extends TestCase +{ + public function testGettersAfterConstructionWithDefault() + { + $from = 'line1a'; + $to = 'line2a'; + $diff = new Diff($from, $to); + + $this->assertSame($from, $diff->getFrom()); + $this->assertSame($to, $diff->getTo()); + $this->assertSame([], $diff->getChunks(), 'Expect chunks to be default value "array()".'); + } + + public function testGettersAfterConstructionWithChunks() + { + $from = 'line1b'; + $to = 'line2b'; + $chunks = [new Chunk(), new Chunk(2, 3)]; + + $diff = new Diff($from, $to, $chunks); + + $this->assertSame($from, $diff->getFrom()); + $this->assertSame($to, $diff->getTo()); + $this->assertSame($chunks, $diff->getChunks(), 'Expect chunks to be passed value.'); + } + + public function testSetChunksAfterConstruction() + { + $diff = new Diff('line1c', 'line2c'); + $this->assertSame([], $diff->getChunks(), 'Expect chunks to be default value "array()".'); + + $chunks = [new Chunk(), new Chunk(2, 3)]; + $diff->setChunks($chunks); + $this->assertSame($chunks, $diff->getChunks(), 'Expect chunks to be passed value.'); + } +} diff --git a/tests/v3_0/DifferTest.php b/tests/v3_0/DifferTest.php new file mode 100644 index 00000000..86c5103f --- /dev/null +++ b/tests/v3_0/DifferTest.php @@ -0,0 +1,462 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0; + +use PHPUnit\Framework\TestCase; +use PhpCsFixer\Diff\v3_0\Output\UnifiedDiffOutputBuilder; + +/** + * @covers PhpCsFixer\Diff\v3_0\Differ + * @covers PhpCsFixer\Diff\v3_0\Output\UnifiedDiffOutputBuilder + * + * @uses PhpCsFixer\Diff\v3_0\MemoryEfficientLongestCommonSubsequenceCalculator + * @uses PhpCsFixer\Diff\v3_0\TimeEfficientLongestCommonSubsequenceCalculator + * @uses PhpCsFixer\Diff\v3_0\Output\AbstractChunkOutputBuilder + */ +final class DifferTest extends TestCase +{ + /** + * @var Differ + */ + private $differ; + + protected function setUp() + { + $this->differ = new Differ; + } + + /** + * @param array $expected + * @param array|string $from + * @param array|string $to + * + * @dataProvider arrayProvider + */ + public function testArrayRepresentationOfDiffCanBeRenderedUsingTimeEfficientLcsImplementation(array $expected, $from, $to) + { + $this->assertSame($expected, $this->differ->diffToArray($from, $to, new TimeEfficientLongestCommonSubsequenceCalculator)); + } + + /** + * @param string $expected + * @param string $from + * @param string $to + * + * @dataProvider textProvider + */ + public function testTextRepresentationOfDiffCanBeRenderedUsingTimeEfficientLcsImplementation($expected, $from, $to) + { + $this->assertSame($expected, $this->differ->diff($from, $to, new TimeEfficientLongestCommonSubsequenceCalculator)); + } + + /** + * @param array $expected + * @param array|string $from + * @param array|string $to + * + * @dataProvider arrayProvider + */ + public function testArrayRepresentationOfDiffCanBeRenderedUsingMemoryEfficientLcsImplementation(array $expected, $from, $to) + { + $this->assertSame($expected, $this->differ->diffToArray($from, $to, new MemoryEfficientLongestCommonSubsequenceCalculator)); + } + + /** + * @param string $expected + * @param string $from + * @param string $to + * + * @dataProvider textProvider + */ + public function testTextRepresentationOfDiffCanBeRenderedUsingMemoryEfficientLcsImplementation($expected, $from, $to) + { + $this->assertSame($expected, $this->differ->diff($from, $to, new MemoryEfficientLongestCommonSubsequenceCalculator)); + } + + public function testTypesOtherThanArrayAndStringCanBePassed() + { + $this->assertSame( + "--- Original\n+++ New\n@@ @@\n-1\n+2\n", + $this->differ->diff(1, 2) + ); + } + + public function testArrayDiffs() + { + $this->assertSame( + '--- Original ++++ New +@@ @@ +-one ++two +', + $this->differ->diff(['one'], ['two']) + ); + } + + public function arrayProvider() + { + return [ + [ + [ + ['a', Differ::REMOVED], + ['b', Differ::ADDED], + ], + 'a', + 'b', + ], + [ + [ + ['ba', Differ::REMOVED], + ['bc', Differ::ADDED], + ], + 'ba', + 'bc', + ], + [ + [ + ['ab', Differ::REMOVED], + ['cb', Differ::ADDED], + ], + 'ab', + 'cb', + ], + [ + [ + ['abc', Differ::REMOVED], + ['adc', Differ::ADDED], + ], + 'abc', + 'adc', + ], + [ + [ + ['ab', Differ::REMOVED], + ['abc', Differ::ADDED], + ], + 'ab', + 'abc', + ], + [ + [ + ['bc', Differ::REMOVED], + ['abc', Differ::ADDED], + ], + 'bc', + 'abc', + ], + [ + [ + ['abc', Differ::REMOVED], + ['abbc', Differ::ADDED], + ], + 'abc', + 'abbc', + ], + [ + [ + ['abcdde', Differ::REMOVED], + ['abcde', Differ::ADDED], + ], + 'abcdde', + 'abcde', + ], + 'same start' => [ + [ + [17, Differ::OLD], + ['b', Differ::REMOVED], + ['d', Differ::ADDED], + ], + [30 => 17, 'a' => 'b'], + [30 => 17, 'c' => 'd'], + ], + 'same end' => [ + [ + [1, Differ::REMOVED], + [2, Differ::ADDED], + ['b', Differ::OLD], + ], + [1 => 1, 'a' => 'b'], + [1 => 2, 'a' => 'b'], + ], + 'same start (2), same end (1)' => [ + [ + [17, Differ::OLD], + [2, Differ::OLD], + [4, Differ::REMOVED], + ['a', Differ::ADDED], + [5, Differ::ADDED], + ['x', Differ::OLD], + ], + [30 => 17, 1 => 2, 2 => 4, 'z' => 'x'], + [30 => 17, 1 => 2, 3 => 'a', 2 => 5, 'z' => 'x'], + ], + 'same' => [ + [ + ['x', Differ::OLD], + ], + ['z' => 'x'], + ['z' => 'x'], + ], + 'diff' => [ + [ + ['y', Differ::REMOVED], + ['x', Differ::ADDED], + ], + ['x' => 'y'], + ['z' => 'x'], + ], + 'diff 2' => [ + [ + ['y', Differ::REMOVED], + ['b', Differ::REMOVED], + ['x', Differ::ADDED], + ['d', Differ::ADDED], + ], + ['x' => 'y', 'a' => 'b'], + ['z' => 'x', 'c' => 'd'], + ], + 'test line diff detection' => [ + [ + [ + "#Warnings contain different line endings!\n", + Differ::DIFF_LINE_END_WARNING, + ], + [ + " [ + [ + [ + "#Warnings contain different line endings!\n", + Differ::DIFF_LINE_END_WARNING, + ], + [ + "expectException(InvalidArgumentException::class); + $this->expectExceptionMessageRegExp('#^"from" must be an array or string\.$#'); + + $this->differ->diffToArray(null, ''); + } + + public function testDiffInvalidToType() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessageRegExp('#^"to" must be an array or string\.$#'); + + $this->differ->diffToArray('', new \stdClass); + } + + /** + * @param array $expected + * @param string $input + * + * @dataProvider provideSplitStringByLinesCases + */ + public function testSplitStringByLines(array $expected, $input) + { + $reflection = new \ReflectionObject($this->differ); + $method = $reflection->getMethod('splitStringByLines'); + $method->setAccessible(true); + + $this->assertSame($expected, $method->invoke($this->differ, $input)); + } + + public function provideSplitStringByLinesCases() + { + return [ + [ + [], + '', + ], + [ + ['a'], + 'a', + ], + [ + ["a\n"], + "a\n", + ], + [ + ["a\r"], + "a\r", + ], + [ + ["a\r\n"], + "a\r\n", + ], + [ + ["\n"], + "\n", + ], + [ + ["\r"], + "\r", + ], + [ + ["\r\n"], + "\r\n", + ], + [ + [ + "A\n", + "B\n", + "\n", + "C\n", + ], + "A\nB\n\nC\n", + ], + [ + [ + "A\r\n", + "B\n", + "\n", + "C\r", + ], + "A\r\nB\n\nC\r", + ], + [ + [ + "\n", + "A\r\n", + "B\n", + "\n", + 'C', + ], + "\nA\r\nB\n\nC", + ], + ]; + } + + public function testConstructorNull() + { + $this->assertAttributeInstanceOf( + UnifiedDiffOutputBuilder::class, + 'outputBuilder', + new Differ(null) + ); + } + + public function testConstructorString() + { + $this->assertAttributeInstanceOf( + UnifiedDiffOutputBuilder::class, + 'outputBuilder', + new Differ("--- Original\n+++ New\n") + ); + } + + public function testConstructorInvalidArgInt() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessageRegExp('/^Expected builder to be an instance of DiffOutputBuilderInterface, or a string, got integer "1"\.$/'); + + new Differ(1); + } + + public function testConstructorInvalidArgObject() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessageRegExp('/^Expected builder to be an instance of DiffOutputBuilderInterface, or a string, got instance of "SplFileInfo"\.$/'); + + new Differ(new \SplFileInfo(__FILE__)); + } +} diff --git a/tests/v3_0/Exception/ConfigurationExceptionTest.php b/tests/v3_0/Exception/ConfigurationExceptionTest.php new file mode 100644 index 00000000..63bfa572 --- /dev/null +++ b/tests/v3_0/Exception/ConfigurationExceptionTest.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0; + +use PHPUnit\Framework\TestCase; + +/** + * @covers PhpCsFixer\Diff\v3_0\ConfigurationException + */ +final class ConfigurationExceptionTest extends TestCase +{ + public function testConstructWithDefaults() + { + $e = new ConfigurationException('test', 'A', 'B'); + + $this->assertSame(0, $e->getCode()); + $this->assertNull($e->getPrevious()); + $this->assertSame('Option "test" must be A, got "string#B".', $e->getMessage()); + } + + public function testConstruct() + { + $e = new ConfigurationException( + 'test', + 'integer', + new \SplFileInfo(__FILE__), + 789, + new \BadMethodCallException(__METHOD__) + ); + + $this->assertSame('Option "test" must be integer, got "SplFileInfo".', $e->getMessage()); + } +} diff --git a/tests/v3_0/Exception/InvalidArgumentExceptionTest.php b/tests/v3_0/Exception/InvalidArgumentExceptionTest.php new file mode 100644 index 00000000..6bce2b63 --- /dev/null +++ b/tests/v3_0/Exception/InvalidArgumentExceptionTest.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0; + +use PHPUnit\Framework\TestCase; + +/** + * @covers PhpCsFixer\Diff\v3_0\InvalidArgumentException + */ +final class InvalidArgumentExceptionTest extends TestCase +{ + public function testInvalidArgumentException() + { + $previousException = new \LogicException(); + $message = 'test'; + $code = 123; + + $exception = new InvalidArgumentException($message, $code, $previousException); + + $this->assertInstanceOf(Exception::class, $exception); + $this->assertSame($message, $exception->getMessage()); + $this->assertSame($code, $exception->getCode()); + $this->assertSame($previousException, $exception->getPrevious()); + } +} diff --git a/tests/v3_0/LineTest.php b/tests/v3_0/LineTest.php new file mode 100644 index 00000000..fe356c09 --- /dev/null +++ b/tests/v3_0/LineTest.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0; + +use PHPUnit\Framework\TestCase; + +/** + * @covers PhpCsFixer\Diff\v3_0\Line + */ +final class LineTest extends TestCase +{ + /** + * @var Line + */ + private $line; + + protected function setUp() + { + $this->line = new Line; + } + + public function testCanBeCreatedWithoutArguments() + { + $this->assertInstanceOf(Line::class, $this->line); + } + + public function testTypeCanBeRetrieved() + { + $this->assertSame(Line::UNCHANGED, $this->line->getType()); + } + + public function testContentCanBeRetrieved() + { + $this->assertSame('', $this->line->getContent()); + } +} diff --git a/tests/v3_0/LongestCommonSubsequenceTest.php b/tests/v3_0/LongestCommonSubsequenceTest.php new file mode 100644 index 00000000..2d074b51 --- /dev/null +++ b/tests/v3_0/LongestCommonSubsequenceTest.php @@ -0,0 +1,201 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0; + +use PHPUnit\Framework\TestCase; + +/** + * @coversNothing + */ +abstract class LongestCommonSubsequenceTest extends TestCase +{ + /** + * @var LongestCommonSubsequenceCalculator + */ + private $implementation; + + /** + * @var string + */ + private $memoryLimit; + + /** + * @var int[] + */ + private $stress_sizes = [1, 2, 3, 100, 500, 1000, 2000]; + + protected function setUp() + { + $this->memoryLimit = \ini_get('memory_limit'); + \ini_set('memory_limit', '256M'); + + $this->implementation = $this->createImplementation(); + } + + protected function tearDown() + { + \ini_set('memory_limit', $this->memoryLimit); + } + + public function testBothEmpty() + { + $from = []; + $to = []; + $common = $this->implementation->calculate($from, $to); + + $this->assertSame([], $common); + } + + public function testIsStrictComparison() + { + $from = [ + false, 0, 0.0, '', null, [], + true, 1, 1.0, 'foo', ['foo', 'bar'], ['foo' => 'bar'], + ]; + $to = $from; + $common = $this->implementation->calculate($from, $to); + + $this->assertSame($from, $common); + + $to = [ + false, false, false, false, false, false, + true, true, true, true, true, true, + ]; + + $expected = [ + false, + true, + ]; + + $common = $this->implementation->calculate($from, $to); + + $this->assertSame($expected, $common); + } + + public function testEqualSequences() + { + foreach ($this->stress_sizes as $size) { + $range = \range(1, $size); + $from = $range; + $to = $range; + $common = $this->implementation->calculate($from, $to); + + $this->assertSame($range, $common); + } + } + + public function testDistinctSequences() + { + $from = ['A']; + $to = ['B']; + $common = $this->implementation->calculate($from, $to); + $this->assertSame([], $common); + + $from = ['A', 'B', 'C']; + $to = ['D', 'E', 'F']; + $common = $this->implementation->calculate($from, $to); + $this->assertSame([], $common); + + foreach ($this->stress_sizes as $size) { + $from = \range(1, $size); + $to = \range($size + 1, $size * 2); + $common = $this->implementation->calculate($from, $to); + $this->assertSame([], $common); + } + } + + public function testCommonSubsequence() + { + $from = ['A', 'C', 'E', 'F', 'G']; + $to = ['A', 'B', 'D', 'E', 'H']; + $expected = ['A', 'E']; + $common = $this->implementation->calculate($from, $to); + $this->assertSame($expected, $common); + + $from = ['A', 'C', 'E', 'F', 'G']; + $to = ['B', 'C', 'D', 'E', 'F', 'H']; + $expected = ['C', 'E', 'F']; + $common = $this->implementation->calculate($from, $to); + $this->assertSame($expected, $common); + + foreach ($this->stress_sizes as $size) { + $from = $size < 2 ? [1] : \range(1, $size + 1, 2); + $to = $size < 3 ? [1] : \range(1, $size + 1, 3); + $expected = $size < 6 ? [1] : \range(1, $size + 1, 6); + $common = $this->implementation->calculate($from, $to); + + $this->assertSame($expected, $common); + } + } + + public function testSingleElementSubsequenceAtStart() + { + foreach ($this->stress_sizes as $size) { + $from = \range(1, $size); + $to = \array_slice($from, 0, 1); + $common = $this->implementation->calculate($from, $to); + + $this->assertSame($to, $common); + } + } + + public function testSingleElementSubsequenceAtMiddle() + { + foreach ($this->stress_sizes as $size) { + $from = \range(1, $size); + $to = \array_slice($from, (int) ($size / 2), 1); + $common = $this->implementation->calculate($from, $to); + + $this->assertSame($to, $common); + } + } + + public function testSingleElementSubsequenceAtEnd() + { + foreach ($this->stress_sizes as $size) { + $from = \range(1, $size); + $to = \array_slice($from, $size - 1, 1); + $common = $this->implementation->calculate($from, $to); + + $this->assertSame($to, $common); + } + } + + public function testReversedSequences() + { + $from = ['A', 'B']; + $to = ['B', 'A']; + $expected = ['A']; + $common = $this->implementation->calculate($from, $to); + $this->assertSame($expected, $common); + + foreach ($this->stress_sizes as $size) { + $from = \range(1, $size); + $to = \array_reverse($from); + $common = $this->implementation->calculate($from, $to); + + $this->assertSame([1], $common); + } + } + + public function testStrictTypeCalculate() + { + $diff = $this->implementation->calculate(['5'], ['05']); + + $this->assertInternalType('array', $diff); + $this->assertCount(0, $diff); + } + + /** + * @return LongestCommonSubsequenceCalculator + */ + abstract protected function createImplementation(); +} diff --git a/tests/v3_0/MemoryEfficientImplementationTest.php b/tests/v3_0/MemoryEfficientImplementationTest.php new file mode 100644 index 00000000..41f1f2f5 --- /dev/null +++ b/tests/v3_0/MemoryEfficientImplementationTest.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0; + +/** + * @covers PhpCsFixer\Diff\v3_0\MemoryEfficientLongestCommonSubsequenceCalculator + */ +final class MemoryEfficientImplementationTest extends LongestCommonSubsequenceTest +{ + protected function createImplementation() + { + return new MemoryEfficientLongestCommonSubsequenceCalculator; + } +} diff --git a/tests/v3_0/Output/AbstractChunkOutputBuilderTest.php b/tests/v3_0/Output/AbstractChunkOutputBuilderTest.php new file mode 100644 index 00000000..3f905edf --- /dev/null +++ b/tests/v3_0/Output/AbstractChunkOutputBuilderTest.php @@ -0,0 +1,154 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0\Output; + +use PHPUnit\Framework\TestCase; +use PhpCsFixer\Diff\v3_0\Differ; + +class TestingAbstractChunkOutputBuilder extends AbstractChunkOutputBuilder { + public function getDiff(array $diff) + { + return ''; + } + + public function getChunks(array $diff, $lineThreshold) + { + return $this->getCommonChunks($diff, $lineThreshold); + } +}; + +/** + * @covers PhpCsFixer\Diff\v3_0\Output\AbstractChunkOutputBuilder + * + * @uses PhpCsFixer\Diff\v3_0\Differ + * @uses PhpCsFixer\Diff\v3_0\Output\UnifiedDiffOutputBuilder + * @uses PhpCsFixer\Diff\v3_0\TimeEfficientLongestCommonSubsequenceCalculator + */ +final class AbstractChunkOutputBuilderTest extends TestCase +{ + /** + * @param array $expected + * @param string $from + * @param string $to + * @param int $lineThreshold + * + * @dataProvider provideGetCommonChunks + */ + public function testGetCommonChunks(array $expected, $from, $to, $lineThreshold = 5) + { + $output = new TestingAbstractChunkOutputBuilder(); + + $this->assertSame( + $expected, + $output->getChunks((new Differ)->diffToArray($from, $to), $lineThreshold) + ); + } + + public function provideGetCommonChunks() + { + return[ + 'same (with default threshold)' => [ + [], + 'A', + 'A', + ], + 'same (threshold 0)' => [ + [0 => 0], + 'A', + 'A', + 0, + ], + 'empty' => [ + [], + '', + '', + ], + 'single line diff' => [ + [], + 'A', + 'B', + ], + 'below threshold I' => [ + [], + "A\nX\nC", + "A\nB\nC", + ], + 'below threshold II' => [ + [], + "A\n\n\n\nX\nC", + "A\n\n\n\nB\nC", + ], + 'below threshold III' => [ + [0 => 5], + "A\n\n\n\n\n\nB", + "A\n\n\n\n\n\nA", + ], + 'same start' => [ + [0 => 5], + "A\n\n\n\n\n\nX\nC", + "A\n\n\n\n\n\nB\nC", + ], + 'same start long' => [ + [0 => 13], + "\n\n\n\n\n\n\n\n\n\n\n\n\n\nA", + "\n\n\n\n\n\n\n\n\n\n\n\n\n\nB", + ], + 'same part in between' => [ + [2 => 8], + "A\n\n\n\n\n\n\nX\nY\nZ\n\n", + "B\n\n\n\n\n\n\nX\nA\nZ\n\n", + ], + 'same trailing' => [ + [2 => 14], + "A\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + "B\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + ], + 'same part in between, same trailing' => [ + [2 => 7, 10 => 15], + "A\n\n\n\n\n\n\nA\n\n\n\n\n\n\n", + "B\n\n\n\n\n\n\nB\n\n\n\n\n\n\n", + ], + 'below custom threshold I' => [ + [], + "A\n\nB", + "A\n\nD", + 2, + ], + 'custom threshold I' => [ + [0 => 1], + "A\n\nB", + "A\n\nD", + 1, + ], + 'custom threshold II' => [ + [], + "A\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + "A\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + 19, + ], + [ + [3 => 9], + "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk", + "a\np\nc\nd\ne\nf\ng\nh\ni\nw\nk", + ], + [ + [0 => 5, 8 => 13], + "A\nA\nA\nA\nA\nA\nX\nC\nC\nC\nC\nC\nC", + "A\nA\nA\nA\nA\nA\nB\nC\nC\nC\nC\nC\nC", + ], + [ + [0 => 5, 8 => 13], + "A\nA\nA\nA\nA\nA\nX\nC\nC\nC\nC\nC\nC\nX", + "A\nA\nA\nA\nA\nA\nB\nC\nC\nC\nC\nC\nC\nY", + ], + ]; + } +} diff --git a/tests/v3_0/Output/DiffOnlyOutputBuilderTest.php b/tests/v3_0/Output/DiffOnlyOutputBuilderTest.php new file mode 100644 index 00000000..9fd0cbee --- /dev/null +++ b/tests/v3_0/Output/DiffOnlyOutputBuilderTest.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0\Output; + +use PHPUnit\Framework\TestCase; +use PhpCsFixer\Diff\v3_0\Differ; + +/** + * @covers PhpCsFixer\Diff\v3_0\Output\DiffOnlyOutputBuilder + * + * @uses PhpCsFixer\Diff\v3_0\Differ + * @uses PhpCsFixer\Diff\v3_0\TimeEfficientLongestCommonSubsequenceCalculator + */ +final class DiffOnlyOutputBuilderTest extends TestCase +{ + /** + * @param string $expected + * @param string $from + * @param string $to + * @param string $header + * + * @dataProvider textForNoNonDiffLinesProvider + */ + public function testDiffDoNotShowNonDiffLines($expected, $from, $to, $header = '') + { + $differ = new Differ(new DiffOnlyOutputBuilder($header)); + + $this->assertSame($expected, $differ->diff($from, $to)); + } + + public function textForNoNonDiffLinesProvider() + { + return [ + [ + " #Warnings contain different line endings!\n-A\r\n+B\n", + "A\r\n", + "B\n", + ], + [ + "-A\n+B\n", + "\nA", + "\nB", + ], + [ + '', + 'a', + 'a', + ], + [ + "-A\n+C\n", + "A\n\n\nB", + "C\n\n\nB", + ], + [ + "header\n", + 'a', + 'a', + 'header', + ], + [ + "header\n", + 'a', + 'a', + "header\n", + ], + ]; + } +} diff --git a/tests/v3_0/Output/Integration/StrictUnifiedDiffOutputBuilderIntegrationTest.php b/tests/v3_0/Output/Integration/StrictUnifiedDiffOutputBuilderIntegrationTest.php new file mode 100644 index 00000000..93e105f7 --- /dev/null +++ b/tests/v3_0/Output/Integration/StrictUnifiedDiffOutputBuilderIntegrationTest.php @@ -0,0 +1,299 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0\Output; + +use PHPUnit\Framework\TestCase; +use PhpCsFixer\Diff\v3_0\Differ; +use PhpCsFixer\Diff\v3_0\Utils\FileUtils; +use PhpCsFixer\Diff\v3_0\Utils\UnifiedDiffAssertTrait; +use Symfony\Component\Process\Process; + +/** + * @covers PhpCsFixer\Diff\v3_0\Output\StrictUnifiedDiffOutputBuilder + * + * @uses PhpCsFixer\Diff\v3_0\Differ + * @uses PhpCsFixer\Diff\v3_0\TimeEfficientLongestCommonSubsequenceCalculator + * + * @requires OS Linux + */ +final class StrictUnifiedDiffOutputBuilderIntegrationTest extends TestCase +{ + use UnifiedDiffAssertTrait; + + private $dir; + + private $fileFrom; + + private $fileTo; + + private $filePatch; + + protected function setUp() + { + $this->dir = \realpath(__DIR__ . '/../../fixtures/out') . '/'; + $this->fileFrom = $this->dir . 'from.txt'; + $this->fileTo = $this->dir . 'to.txt'; + $this->filePatch = $this->dir . 'diff.patch'; + + if (!\is_dir($this->dir)) { + throw new \RuntimeException('Integration test working directory not found.'); + } + + $this->cleanUpTempFiles(); + } + + protected function tearDown() + { + $this->cleanUpTempFiles(); + } + + /** + * Integration test + * + * - get a file pair + * - create a `diff` between the files + * - test applying the diff using `git apply` + * - test applying the diff using `patch` + * + * @param string $fileFrom + * @param string $fileTo + * + * @dataProvider provideFilePairs + */ + public function testIntegrationUsingPHPFileInVendorGitApply($fileFrom, $fileTo) + { + $from = FileUtils::getFileContent($fileFrom); + $to = FileUtils::getFileContent($fileTo); + + $diff = (new Differ(new StrictUnifiedDiffOutputBuilder(['fromFile' => 'Original', 'toFile' => 'New'])))->diff($from, $to); + + if ('' === $diff && $from === $to) { + // odd case: test after executing as it is more efficient than to read the files and check the contents every time + $this->addToAssertionCount(1); + + return; + } + + $this->doIntegrationTestGitApply($diff, $from, $to); + } + + /** + * Integration test + * + * - get a file pair + * - create a `diff` between the files + * - test applying the diff using `git apply` + * - test applying the diff using `patch` + * + * @param string $fileFrom + * @param string $fileTo + * + * @dataProvider provideFilePairs + */ + public function testIntegrationUsingPHPFileInVendorPatch($fileFrom, $fileTo) + { + $from = FileUtils::getFileContent($fileFrom); + $to = FileUtils::getFileContent($fileTo); + + $diff = (new Differ(new StrictUnifiedDiffOutputBuilder(['fromFile' => 'Original', 'toFile' => 'New'])))->diff($from, $to); + + if ('' === $diff && $from === $to) { + // odd case: test after executing as it is more efficient than to read the files and check the contents every time + $this->addToAssertionCount(1); + + return; + } + + $this->doIntegrationTestPatch($diff, $from, $to); + } + + /** + * @param string $expected + * @param string $from + * @param string $to + * + * @dataProvider provideOutputBuildingCases + * @dataProvider provideSample + * @dataProvider provideBasicDiffGeneration + */ + public function testIntegrationOfUnitTestCasesGitApply($expected, $from, $to) + { + $this->doIntegrationTestGitApply($expected, $from, $to); + } + + /** + * @param string $expected + * @param string $from + * @param string $to + * + * @dataProvider provideOutputBuildingCases + * @dataProvider provideSample + * @dataProvider provideBasicDiffGeneration + */ + public function testIntegrationOfUnitTestCasesPatch($expected, $from, $to) + { + $this->doIntegrationTestPatch($expected, $from, $to); + } + + public function provideOutputBuildingCases() + { + return StrictUnifiedDiffOutputBuilderDataProvider::provideOutputBuildingCases(); + } + + public function provideSample() + { + return StrictUnifiedDiffOutputBuilderDataProvider::provideSample(); + } + + public function provideBasicDiffGeneration() + { + return StrictUnifiedDiffOutputBuilderDataProvider::provideBasicDiffGeneration(); + } + + public function provideFilePairs() + { + $cases = []; + $fromFile = __FILE__; + $vendorDir = \realpath(__DIR__ . '/../../../../vendor'); + + $fileIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($vendorDir, \RecursiveDirectoryIterator::SKIP_DOTS)); + + /** @var \SplFileInfo $file */ + foreach ($fileIterator as $file) { + if ('php' !== $file->getExtension()) { + continue; + } + + $toFile = $file->getPathname(); + $cases[\sprintf("Diff file:\n\"%s\"\nvs.\n\"%s\"\n", \realpath($fromFile), \realpath($toFile))] = [$fromFile, $toFile]; + $fromFile = $toFile; + } + + return $cases; + } + + /** + * Compare diff create by builder and against one create by `diff` command. + * + * @param string $diff + * @param string $from + * @param string $to + * + * @dataProvider provideBasicDiffGeneration + */ + public function testIntegrationDiffOutputBuilderVersusDiffCommand($diff, $from, $to) + { + $this->assertNotSame('', $diff); + $this->assertValidUnifiedDiffFormat($diff); + + $this->assertNotFalse(\file_put_contents($this->fileFrom, $from)); + $this->assertNotFalse(\file_put_contents($this->fileTo, $to)); + + $p = new Process(\sprintf('diff -u %s %s', \escapeshellarg($this->fileFrom), \escapeshellarg($this->fileTo))); + $p->run(); + $this->assertSame(1, $p->getExitCode()); // note: Process assumes exit code 0 for `isSuccessful`, however `diff` uses the exit code `1` for success with diff + + $output = $p->getOutput(); + + $diffLines = \preg_split('/(.*\R)/', $diff, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + $diffLines[0] = \preg_replace('#^\-\-\- .*#', '--- /' . $this->fileFrom, $diffLines[0], 1); + $diffLines[1] = \preg_replace('#^\+\+\+ .*#', '+++ /' . $this->fileFrom, $diffLines[1], 1); + $diff = \implode('', $diffLines); + + $outputLines = \preg_split('/(.*\R)/', $output, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + $outputLines[0] = \preg_replace('#^\-\-\- .*#', '--- /' . $this->fileFrom, $outputLines[0], 1); + $outputLines[1] = \preg_replace('#^\+\+\+ .*#', '+++ /' . $this->fileFrom, $outputLines[1], 1); + $output = \implode('', $outputLines); + + $this->assertSame($diff, $output); + } + + private function doIntegrationTestGitApply($diff, $from, $to) + { + $this->assertNotSame('', $diff); + $this->assertValidUnifiedDiffFormat($diff); + + $diff = self::setDiffFileHeader($diff, $this->fileFrom); + + $this->assertNotFalse(\file_put_contents($this->fileFrom, $from)); + $this->assertNotFalse(\file_put_contents($this->filePatch, $diff)); + + $p = new Process(\sprintf( + 'git --git-dir %s apply --check -v --unsafe-paths --ignore-whitespace %s', + \escapeshellarg($this->dir), + \escapeshellarg($this->filePatch) + )); + + $p->run(); + + $this->assertProcessSuccessful($p); + } + + private function doIntegrationTestPatch($diff, $from, $to) + { + $this->assertNotSame('', $diff); + $this->assertValidUnifiedDiffFormat($diff); + + $diff = self::setDiffFileHeader($diff, $this->fileFrom); + + $this->assertNotFalse(\file_put_contents($this->fileFrom, $from)); + $this->assertNotFalse(\file_put_contents($this->filePatch, $diff)); + + $command = \sprintf( + 'patch -u --verbose --posix %s < %s', + \escapeshellarg($this->fileFrom), + \escapeshellarg($this->filePatch) + ); + + $p = new Process($command); + $p->run(); + + $this->assertProcessSuccessful($p); + + $this->assertStringEqualsFile( + $this->fileFrom, + $to, + \sprintf('Patch command "%s".', $command) + ); + } + + private function assertProcessSuccessful(Process $p) + { + $this->assertTrue( + $p->isSuccessful(), + \sprintf( + "Command exec. was not successful:\n\"%s\"\nOutput:\n\"%s\"\nStdErr:\n\"%s\"\nExit code %d.\n", + $p->getCommandLine(), + $p->getOutput(), + $p->getErrorOutput(), + $p->getExitCode() + ) + ); + } + + private function cleanUpTempFiles() + { + @\unlink($this->fileFrom . '.orig'); + @\unlink($this->fileFrom . '.rej'); + @\unlink($this->fileFrom); + @\unlink($this->fileTo); + @\unlink($this->filePatch); + } + + private static function setDiffFileHeader($diff, $file) + { + $diffLines = \preg_split('/(.*\R)/', $diff, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + $diffLines[0] = \preg_replace('#^\-\-\- .*#', '--- /' . $file, $diffLines[0], 1); + $diffLines[1] = \preg_replace('#^\+\+\+ .*#', '+++ /' . $file, $diffLines[1], 1); + + return \implode('', $diffLines); + } +} diff --git a/tests/v3_0/Output/Integration/UnifiedDiffOutputBuilderIntegrationTest.php b/tests/v3_0/Output/Integration/UnifiedDiffOutputBuilderIntegrationTest.php new file mode 100644 index 00000000..c6b51ab0 --- /dev/null +++ b/tests/v3_0/Output/Integration/UnifiedDiffOutputBuilderIntegrationTest.php @@ -0,0 +1,163 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0\Output; + +use PHPUnit\Framework\TestCase; +use PhpCsFixer\Diff\v3_0\Utils\UnifiedDiffAssertTrait; +use Symfony\Component\Process\Process; + +/** + * @covers PhpCsFixer\Diff\v3_0\Output\UnifiedDiffOutputBuilder + * + * @uses PhpCsFixer\Diff\v3_0\Differ + * @uses PhpCsFixer\Diff\v3_0\TimeEfficientLongestCommonSubsequenceCalculator + * + * @requires OS Linux + */ +final class UnifiedDiffOutputBuilderIntegrationTest extends TestCase +{ + use UnifiedDiffAssertTrait; + + private $dir; + + private $fileFrom; + + private $filePatch; + + protected function setUp() + { + $this->dir = \realpath(__DIR__ . '/../../fixtures/out/') . '/'; + $this->fileFrom = $this->dir . 'from.txt'; + $this->filePatch = $this->dir . 'patch.txt'; + + $this->cleanUpTempFiles(); + } + + protected function tearDown() + { + $this->cleanUpTempFiles(); + } + + /** + * @dataProvider provideDiffWithLineNumbers + * + * @param mixed $expected + * @param mixed $from + * @param mixed $to + */ + public function testDiffWithLineNumbersPath($expected, $from, $to) + { + $this->doIntegrationTestPatch($expected, $from, $to); + } + + /** + * @dataProvider provideDiffWithLineNumbers + * + * @param mixed $expected + * @param mixed $from + * @param mixed $to + */ + public function testDiffWithLineNumbersGitApply($expected, $from, $to) + { + $this->doIntegrationTestGitApply($expected, $from, $to); + } + + public function provideDiffWithLineNumbers() + { + return \array_filter( + UnifiedDiffOutputBuilderDataProvider::provideDiffWithLineNumbers(), + static function ($key) { + return !\is_string($key) || false === \strpos($key, 'non_patch_compat'); + }, + ARRAY_FILTER_USE_KEY + ); + } + + private function doIntegrationTestPatch($diff, $from, $to) + { + $this->assertNotSame('', $diff); + $this->assertValidUnifiedDiffFormat($diff); + + $diff = self::setDiffFileHeader($diff, $this->fileFrom); + + $this->assertNotFalse(\file_put_contents($this->fileFrom, $from)); + $this->assertNotFalse(\file_put_contents($this->filePatch, $diff)); + + $command = \sprintf( + 'patch -u --verbose --posix %s < %s', // --posix + \escapeshellarg($this->fileFrom), + \escapeshellarg($this->filePatch) + ); + + $p = new Process($command); + $p->run(); + + $this->assertProcessSuccessful($p); + + $this->assertStringEqualsFile( + $this->fileFrom, + $to, + \sprintf('Patch command "%s".', $command) + ); + } + + private function doIntegrationTestGitApply($diff, $from, $to) + { + $this->assertNotSame('', $diff); + $this->assertValidUnifiedDiffFormat($diff); + + $diff = self::setDiffFileHeader($diff, $this->fileFrom); + + $this->assertNotFalse(\file_put_contents($this->fileFrom, $from)); + $this->assertNotFalse(\file_put_contents($this->filePatch, $diff)); + + $command = \sprintf( + 'git --git-dir %s apply --check -v --unsafe-paths --ignore-whitespace %s', + \escapeshellarg($this->dir), + \escapeshellarg($this->filePatch) + ); + + $p = new Process($command); + $p->run(); + + $this->assertProcessSuccessful($p); + } + + private function assertProcessSuccessful(Process $p) + { + $this->assertTrue( + $p->isSuccessful(), + \sprintf( + "Command exec. was not successful:\n\"%s\"\nOutput:\n\"%s\"\nStdErr:\n\"%s\"\nExit code %d.\n", + $p->getCommandLine(), + $p->getOutput(), + $p->getErrorOutput(), + $p->getExitCode() + ) + ); + } + + private function cleanUpTempFiles() + { + @\unlink($this->fileFrom . '.orig'); + @\unlink($this->fileFrom); + @\unlink($this->filePatch); + } + + private static function setDiffFileHeader($diff, $file) + { + $diffLines = \preg_split('/(.*\R)/', $diff, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + $diffLines[0] = \preg_replace('#^\-\-\- .*#', '--- /' . $file, $diffLines[0], 1); + $diffLines[1] = \preg_replace('#^\+\+\+ .*#', '+++ /' . $file, $diffLines[1], 1); + + return \implode('', $diffLines); + } +} diff --git a/tests/v3_0/Output/StrictUnifiedDiffOutputBuilderDataProvider.php b/tests/v3_0/Output/StrictUnifiedDiffOutputBuilderDataProvider.php new file mode 100644 index 00000000..984d7401 --- /dev/null +++ b/tests/v3_0/Output/StrictUnifiedDiffOutputBuilderDataProvider.php @@ -0,0 +1,189 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0\Output; + +final class StrictUnifiedDiffOutputBuilderDataProvider +{ + public static function provideOutputBuildingCases() + { + return [ + [ +'--- input.txt ++++ output.txt +@@ -1,3 +1,4 @@ ++b + ' . ' + ' . ' + ' . ' +@@ -16,5 +17,4 @@ + ' . ' + ' . ' + ' . ' +- +-B ++A +', + "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nB\n", + "b\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nA\n", + [ + 'fromFile' => 'input.txt', + 'toFile' => 'output.txt', + ], + ], + [ +'--- ' . __FILE__ . "\t2017-10-02 17:38:11.586413675 +0100 ++++ output1.txt\t2017-10-03 12:09:43.086719482 +0100 +@@ -1,1 +1,1 @@ +-B ++X +", + "B\n", + "X\n", + [ + 'fromFile' => __FILE__, + 'fromFileDate' => '2017-10-02 17:38:11.586413675 +0100', + 'toFile' => 'output1.txt', + 'toFileDate' => '2017-10-03 12:09:43.086719482 +0100', + 'collapseRanges' => false, + ], + ], + [ +'--- input.txt ++++ output.txt +@@ -1 +1 @@ +-B ++X +', + "B\n", + "X\n", + [ + 'fromFile' => 'input.txt', + 'toFile' => 'output.txt', + 'collapseRanges' => true, + ], + ], + ]; + } + + public static function provideSample() + { + return [ + [ +'--- input.txt ++++ output.txt +@@ -1,6 +1,6 @@ + 1 + 2 + 3 +-4 ++X + 5 + 6 +', + "1\n2\n3\n4\n5\n6\n", + "1\n2\n3\nX\n5\n6\n", + [ + 'fromFile' => 'input.txt', + 'toFile' => 'output.txt', + ], + ], + ]; + } + + public static function provideBasicDiffGeneration() + { + return [ + [ +"--- input.txt ++++ output.txt +@@ -1,2 +1 @@ +-A +-B ++A\rB +", + "A\nB\n", + "A\rB\n", + ], + [ +"--- input.txt ++++ output.txt +@@ -1 +1 @@ +- ++\r +\\ No newline at end of file +", + "\n", + "\r", + ], + [ +"--- input.txt ++++ output.txt +@@ -1 +1 @@ +-\r +\\ No newline at end of file ++ +", + "\r", + "\n", + ], + [ +'--- input.txt ++++ output.txt +@@ -1,3 +1,3 @@ + X + A +-A ++B +', + "X\nA\nA\n", + "X\nA\nB\n", + ], + [ +'--- input.txt ++++ output.txt +@@ -1,3 +1,3 @@ + X + A +-A +\ No newline at end of file ++B +', + "X\nA\nA", + "X\nA\nB\n", + ], + [ +'--- input.txt ++++ output.txt +@@ -1,3 +1,3 @@ + A + A +-A ++B +\ No newline at end of file +', + "A\nA\nA\n", + "A\nA\nB", + ], + [ +'--- input.txt ++++ output.txt +@@ -1 +1 @@ +-A +\ No newline at end of file ++B +\ No newline at end of file +', + 'A', + 'B', + ], + ]; + } +} diff --git a/tests/v3_0/Output/StrictUnifiedDiffOutputBuilderTest.php b/tests/v3_0/Output/StrictUnifiedDiffOutputBuilderTest.php new file mode 100644 index 00000000..2131cd41 --- /dev/null +++ b/tests/v3_0/Output/StrictUnifiedDiffOutputBuilderTest.php @@ -0,0 +1,684 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0\Output; + +use PHPUnit\Framework\TestCase; +use PhpCsFixer\Diff\v3_0\ConfigurationException; +use PhpCsFixer\Diff\v3_0\Differ; +use PhpCsFixer\Diff\v3_0\Utils\UnifiedDiffAssertTrait; + +/** + * @covers PhpCsFixer\Diff\v3_0\Output\StrictUnifiedDiffOutputBuilder + * + * @uses PhpCsFixer\Diff\v3_0\Differ + */ +final class StrictUnifiedDiffOutputBuilderTest extends TestCase +{ + use UnifiedDiffAssertTrait; + + /** + * @param string $expected + * @param string $from + * @param string $to + * @param array $options + * + * @dataProvider provideOutputBuildingCases + */ + public function testOutputBuilding($expected, $from, $to, array $options) + { + $diff = $this->getDiffer($options)->diff($from, $to); + + $this->assertValidDiffFormat($diff); + $this->assertSame($expected, $diff); + } + + /** + * @param string $expected + * @param string $from + * @param string $to + * @param array $options + * + * @dataProvider provideSample + */ + public function testSample($expected, $from, $to, array $options) + { + $diff = $this->getDiffer($options)->diff($from, $to); + + $this->assertValidDiffFormat($diff); + $this->assertSame($expected, $diff); + } + + /** + * {@inheritdoc} + */ + public function assertValidDiffFormat($diff) + { + $this->assertValidUnifiedDiffFormat($diff); + } + + /** + * {@inheritdoc} + */ + public function provideOutputBuildingCases() + { + return StrictUnifiedDiffOutputBuilderDataProvider::provideOutputBuildingCases(); + } + + /** + * {@inheritdoc} + */ + public function provideSample() + { + return StrictUnifiedDiffOutputBuilderDataProvider::provideSample(); + } + + /** + * @param string $expected + * @param string $from + * @param string $to + * + * @dataProvider provideBasicDiffGeneration + */ + public function testBasicDiffGeneration($expected, $from, $to) + { + $diff = $this->getDiffer([ + 'fromFile' => 'input.txt', + 'toFile' => 'output.txt', + ])->diff($from, $to); + + $this->assertValidDiffFormat($diff); + $this->assertSame($expected, $diff); + } + + public function provideBasicDiffGeneration() + { + return StrictUnifiedDiffOutputBuilderDataProvider::provideBasicDiffGeneration(); + } + + /** + * @param string $expected + * @param string $from + * @param string $to + * @param array $config + * + * @dataProvider provideConfiguredDiffGeneration + */ + public function testConfiguredDiffGeneration($expected, $from, $to, array $config = []) + { + $diff = $this->getDiffer(\array_merge([ + 'fromFile' => 'input.txt', + 'toFile' => 'output.txt', + ], $config))->diff($from, $to); + + $this->assertValidDiffFormat($diff); + $this->assertSame($expected, $diff); + } + + public function provideConfiguredDiffGeneration() + { + return [ + [ + '--- input.txt ++++ output.txt +@@ -1 +1 @@ +-a +\ No newline at end of file ++b +\ No newline at end of file +', + 'a', + 'b', + ], + [ + '', + "1\n2", + "1\n2", + ], + [ + '', + "1\n", + "1\n", + ], + [ +'--- input.txt ++++ output.txt +@@ -4 +4 @@ +-X ++4 +', + "1\n2\n3\nX\n5\n6\n7\n8\n9\n0\n", + "1\n2\n3\n4\n5\n6\n7\n8\n9\n0\n", + [ + 'contextLines' => 0, + ], + ], + [ +'--- input.txt ++++ output.txt +@@ -3,3 +3,3 @@ + 3 +-X ++4 + 5 +', + "1\n2\n3\nX\n5\n6\n7\n8\n9\n0\n", + "1\n2\n3\n4\n5\n6\n7\n8\n9\n0\n", + [ + 'contextLines' => 1, + ], + ], + [ +'--- input.txt ++++ output.txt +@@ -1,10 +1,10 @@ + 1 + 2 + 3 +-X ++4 + 5 + 6 + 7 + 8 + 9 + 0 +', + "1\n2\n3\nX\n5\n6\n7\n8\n9\n0\n", + "1\n2\n3\n4\n5\n6\n7\n8\n9\n0\n", + [ + 'contextLines' => 999, + ], + ], + [ +'--- input.txt ++++ output.txt +@@ -1,0 +1,2 @@ ++ ++A +', + '', + "\nA\n", + ], + [ +'--- input.txt ++++ output.txt +@@ -1,2 +1,0 @@ +- +-A +', + "\nA\n", + '', + ], + [ + '--- input.txt ++++ output.txt +@@ -1,5 +1,5 @@ + 1 +-X ++2 + 3 +-Y ++4 + 5 +@@ -8,3 +8,3 @@ + 8 +-X ++9 + 0 +', + "1\nX\n3\nY\n5\n6\n7\n8\nX\n0\n", + "1\n2\n3\n4\n5\n6\n7\n8\n9\n0\n", + [ + 'commonLineThreshold' => 2, + 'contextLines' => 1, + ], + ], + [ + '--- input.txt ++++ output.txt +@@ -2 +2 @@ +-X ++2 +@@ -4 +4 @@ +-Y ++4 +@@ -9 +9 @@ +-X ++9 +', + "1\nX\n3\nY\n5\n6\n7\n8\nX\n0\n", + "1\n2\n3\n4\n5\n6\n7\n8\n9\n0\n", + [ + 'commonLineThreshold' => 1, + 'contextLines' => 0, + ], + ], + ]; + } + + public function testReUseBuilder() + { + $differ = $this->getDiffer([ + 'fromFile' => 'input.txt', + 'toFile' => 'output.txt', + ]); + + $diff = $differ->diff("A\nB\n", "A\nX\n"); + $this->assertSame( +'--- input.txt ++++ output.txt +@@ -1,2 +1,2 @@ + A +-B ++X +', + $diff + ); + + $diff = $differ->diff("A\n", "A\n"); + $this->assertSame( + '', + $diff + ); + } + + public function testEmptyDiff() + { + $builder = new StrictUnifiedDiffOutputBuilder([ + 'fromFile' => 'input.txt', + 'toFile' => 'output.txt', + ]); + + $this->assertSame( + '', + $builder->getDiff([]) + ); + } + + /** + * @param array $options + * @param string $message + * + * @dataProvider provideInvalidConfiguration + */ + public function testInvalidConfiguration(array $options, $message) + { + $this->expectException(ConfigurationException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote($message, '#'))); + + new StrictUnifiedDiffOutputBuilder($options); + } + + public function provideInvalidConfiguration() + { + $time = \time(); + + return [ + [ + ['collapseRanges' => 1], + 'Option "collapseRanges" must be a bool, got "integer#1".', + ], + [ + ['contextLines' => 'a'], + 'Option "contextLines" must be an int >= 0, got "string#a".', + ], + [ + ['commonLineThreshold' => -2], + 'Option "commonLineThreshold" must be an int > 0, got "integer#-2".', + ], + [ + ['commonLineThreshold' => 0], + 'Option "commonLineThreshold" must be an int > 0, got "integer#0".', + ], + [ + ['fromFile' => new \SplFileInfo(__FILE__)], + 'Option "fromFile" must be a string, got "SplFileInfo".', + ], + [ + ['fromFile' => null], + 'Option "fromFile" must be a string, got "".', + ], + [ + [ + 'fromFile' => __FILE__, + 'toFile' => 1, + ], + 'Option "toFile" must be a string, got "integer#1".', + ], + [ + [ + 'fromFile' => __FILE__, + 'toFile' => __FILE__, + 'toFileDate' => $time, + ], + 'Option "toFileDate" must be a string or , got "integer#' . $time . '".', + ], + [ + [], + 'Option "fromFile" must be a string, got "".', + ], + ]; + } + + /** + * @param string $expected + * @param string $from + * @param string $to + * @param int $threshold + * + * @dataProvider provideCommonLineThresholdCases + */ + public function testCommonLineThreshold($expected, $from, $to, $threshold) + { + $diff = $this->getDiffer([ + 'fromFile' => 'input.txt', + 'toFile' => 'output.txt', + 'commonLineThreshold' => $threshold, + 'contextLines' => 0, + ])->diff($from, $to); + + $this->assertValidDiffFormat($diff); + $this->assertSame($expected, $diff); + } + + public function provideCommonLineThresholdCases() + { + return [ + [ +'--- input.txt ++++ output.txt +@@ -2,3 +2,3 @@ +-X ++B + C12 +-Y ++D +@@ -7 +7 @@ +-X ++Z +', + "A\nX\nC12\nY\nA\nA\nX\n", + "A\nB\nC12\nD\nA\nA\nZ\n", + 2, + ], + [ +'--- input.txt ++++ output.txt +@@ -2 +2 @@ +-X ++B +@@ -4 +4 @@ +-Y ++D +', + "A\nX\nV\nY\n", + "A\nB\nV\nD\n", + 1, + ], + ]; + } + + /** + * @param string $expected + * @param string $from + * @param string $to + * @param int $contextLines + * @param int $commonLineThreshold + * + * @dataProvider provideContextLineConfigurationCases + */ + public function testContextLineConfiguration($expected, $from, $to, $contextLines, $commonLineThreshold = 6) + { + $diff = $this->getDiffer([ + 'fromFile' => 'input.txt', + 'toFile' => 'output.txt', + 'contextLines' => $contextLines, + 'commonLineThreshold' => $commonLineThreshold, + ])->diff($from, $to); + + $this->assertValidDiffFormat($diff); + $this->assertSame($expected, $diff); + } + + public function provideContextLineConfigurationCases() + { + $from = "A\nB\nC\nD\nE\nF\nX\nG\nH\nI\nJ\nK\nL\nM\n"; + $to = "A\nB\nC\nD\nE\nF\nY\nG\nH\nI\nJ\nK\nL\nM\n"; + + return [ + 'EOF 0' => [ + "--- input.txt\n+++ output.txt\n@@ -3 +3 @@ +-X +\\ No newline at end of file ++Y +\\ No newline at end of file +", + "A\nB\nX", + "A\nB\nY", + 0, + ], + 'EOF 1' => [ + "--- input.txt\n+++ output.txt\n@@ -2,2 +2,2 @@ + B +-X +\\ No newline at end of file ++Y +\\ No newline at end of file +", + "A\nB\nX", + "A\nB\nY", + 1, +], + 'EOF 2' => [ + "--- input.txt\n+++ output.txt\n@@ -1,3 +1,3 @@ + A + B +-X +\\ No newline at end of file ++Y +\\ No newline at end of file +", + "A\nB\nX", + "A\nB\nY", + 2, + ], + 'EOF 200' => [ + "--- input.txt\n+++ output.txt\n@@ -1,3 +1,3 @@ + A + B +-X +\\ No newline at end of file ++Y +\\ No newline at end of file +", + "A\nB\nX", + "A\nB\nY", + 200, + ], + 'n/a 0' => [ + "--- input.txt\n+++ output.txt\n@@ -7 +7 @@\n-X\n+Y\n", + $from, + $to, + 0, + ], + 'G' => [ + "--- input.txt\n+++ output.txt\n@@ -6,3 +6,3 @@\n F\n-X\n+Y\n G\n", + $from, + $to, + 1, + ], + 'H' => [ + "--- input.txt\n+++ output.txt\n@@ -5,5 +5,5 @@\n E\n F\n-X\n+Y\n G\n H\n", + $from, + $to, + 2, + ], + 'I' => [ + "--- input.txt\n+++ output.txt\n@@ -4,7 +4,7 @@\n D\n E\n F\n-X\n+Y\n G\n H\n I\n", + $from, + $to, + 3, + ], + 'J' => [ + "--- input.txt\n+++ output.txt\n@@ -3,9 +3,9 @@\n C\n D\n E\n F\n-X\n+Y\n G\n H\n I\n J\n", + $from, + $to, + 4, + ], + 'K' => [ + "--- input.txt\n+++ output.txt\n@@ -2,11 +2,11 @@\n B\n C\n D\n E\n F\n-X\n+Y\n G\n H\n I\n J\n K\n", + $from, + $to, + 5, + ], + 'L' => [ + "--- input.txt\n+++ output.txt\n@@ -1,13 +1,13 @@\n A\n B\n C\n D\n E\n F\n-X\n+Y\n G\n H\n I\n J\n K\n L\n", + $from, + $to, + 6, + ], + 'M' => [ + "--- input.txt\n+++ output.txt\n@@ -1,14 +1,14 @@\n A\n B\n C\n D\n E\n F\n-X\n+Y\n G\n H\n I\n J\n K\n L\n M\n", + $from, + $to, + 7, + ], + 'M no linebreak EOF .1' => [ + "--- input.txt\n+++ output.txt\n@@ -1,14 +1,14 @@\n A\n B\n C\n D\n E\n F\n-X\n+Y\n G\n H\n I\n J\n K\n L\n-M\n+M\n\\ No newline at end of file\n", + $from, + \substr($to, 0, -1), + 7, + ], + 'M no linebreak EOF .2' => [ + "--- input.txt\n+++ output.txt\n@@ -1,14 +1,14 @@\n A\n B\n C\n D\n E\n F\n-X\n+Y\n G\n H\n I\n J\n K\n L\n-M\n\\ No newline at end of file\n+M\n", + \substr($from, 0, -1), + $to, + 7, + ], + 'M no linebreak EOF .3' => [ + "--- input.txt\n+++ output.txt\n@@ -1,14 +1,14 @@\n A\n B\n C\n D\n E\n F\n-X\n+Y\n G\n H\n I\n J\n K\n L\n M\n", + \substr($from, 0, -1), + \substr($to, 0, -1), + 7, + ], + 'M no linebreak EOF .4' => [ + "--- input.txt\n+++ output.txt\n@@ -1,14 +1,14 @@\n A\n B\n C\n D\n E\n F\n-X\n+Y\n G\n H\n I\n J\n K\n L\n M\n\\ No newline at end of file\n", + \substr($from, 0, -1), + \substr($to, 0, -1), + 10000, + 10000, + ], + 'M+1' => [ + "--- input.txt\n+++ output.txt\n@@ -1,14 +1,14 @@\n A\n B\n C\n D\n E\n F\n-X\n+Y\n G\n H\n I\n J\n K\n L\n M\n", + $from, + $to, + 8, + ], + 'M+100' => [ + "--- input.txt\n+++ output.txt\n@@ -1,14 +1,14 @@\n A\n B\n C\n D\n E\n F\n-X\n+Y\n G\n H\n I\n J\n K\n L\n M\n", + $from, + $to, + 107, + ], + '0 II' => [ + "--- input.txt\n+++ output.txt\n@@ -12 +12 @@\n-X\n+Y\n", + "A\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nX\nM\n", + "A\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nY\nM\n", + 0, + 999, + ], + '0\' II' => [ + "--- input.txt\n+++ output.txt\n@@ -12 +12 @@\n-X\n+Y\n", + "A\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nX\nM\nA\nA\nA\nA\nA\n", + "A\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nY\nM\nA\nA\nA\nA\nA\n", + 0, + 999, + ], + '0\'\' II' => [ + "--- input.txt\n+++ output.txt\n@@ -12,2 +12,2 @@\n-X\n-M\n\\ No newline at end of file\n+Y\n+M\n", + "A\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nX\nM", + "A\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nY\nM\n", + 0, + ], + '0\'\'\' II' => [ + "--- input.txt\n+++ output.txt\n@@ -12,2 +12,2 @@\n-X\n-X1\n+Y\n+Y2\n", + "A\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nX\nX1\nM\nA\nA\nA\nA\nA\n", + "A\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nY\nY2\nM\nA\nA\nA\nA\nA\n", + 0, + 999, + ], + '1 II' => [ + "--- input.txt\n+++ output.txt\n@@ -11,3 +11,3 @@\n K\n-X\n+Y\n M\n", + "A\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nX\nM\n", + "A\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nY\nM\n", + 1, + ], + '5 II' => [ + "--- input.txt\n+++ output.txt\n@@ -7,7 +7,7 @@\n G\n H\n I\n J\n K\n-X\n+Y\n M\n", + "A\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nX\nM\n", + "A\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nY\nM\n", + 5, + ], + [ + '--- input.txt ++++ output.txt +@@ -1,28 +1,28 @@ + A +-X ++B + V +-Y ++D + 1 + A + 2 + A + 3 + A + 4 + A + 8 + A + 9 + A + 5 + A + A + A + A + A + A + A + A + A + A + A +', + "A\nX\nV\nY\n1\nA\n2\nA\n3\nA\n4\nA\n8\nA\n9\nA\n5\nA\nA\nA\nA\nA\nA\nA\nA\nA\nA\nA\n", + "A\nB\nV\nD\n1\nA\n2\nA\n3\nA\n4\nA\n8\nA\n9\nA\n5\nA\nA\nA\nA\nA\nA\nA\nA\nA\nA\nA\n", + 9999, + 99999, + ], + ]; + } + + /** + * Returns a new instance of a Differ with a new instance of the class (DiffOutputBuilderInterface) under test. + * + * @param array $options + * + * @return Differ + */ + private function getDiffer(array $options = []) + { + return new Differ(new StrictUnifiedDiffOutputBuilder($options)); + } +} diff --git a/tests/v3_0/Output/UnifiedDiffOutputBuilderDataProvider.php b/tests/v3_0/Output/UnifiedDiffOutputBuilderDataProvider.php new file mode 100644 index 00000000..da5dc271 --- /dev/null +++ b/tests/v3_0/Output/UnifiedDiffOutputBuilderDataProvider.php @@ -0,0 +1,396 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0\Output; + +final class UnifiedDiffOutputBuilderDataProvider +{ + public static function provideDiffWithLineNumbers() + { + return [ + 'diff line 1 non_patch_compat' => [ +'--- Original ++++ New +@@ -1 +1 @@ +-AA ++BA +', + 'AA', + 'BA', + ], + 'diff line +1 non_patch_compat' => [ +'--- Original ++++ New +@@ -1 +1,2 @@ +-AZ ++ ++B +', + 'AZ', + "\nB", + ], + 'diff line -1 non_patch_compat' => [ +'--- Original ++++ New +@@ -1,2 +1 @@ +- +-AF ++B +', + "\nAF", + 'B', + ], + 'II non_patch_compat' => [ +'--- Original ++++ New +@@ -1,4 +1,2 @@ +- +- + A + 1 +', + "\n\nA\n1", + "A\n1", + ], + 'diff last line II - no trailing linebreak non_patch_compat' => [ +'--- Original ++++ New +@@ -5,4 +5,4 @@ + ' . ' + ' . ' + ' . ' +-E ++B +', + "A\n\n\n\n\n\n\nE", + "A\n\n\n\n\n\n\nB", + ], + [ + "--- Original\n+++ New\n@@ -1,2 +1 @@\n \n-\n", + "\n\n", + "\n", + ], + 'diff line endings non_patch_compat' => [ + "--- Original\n+++ New\n@@ -1 +1 @@\n #Warnings contain different line endings!\n- [ +'--- Original ++++ New +', + "AT\n", + "AT\n", + ], + [ +'--- Original ++++ New +@@ -1,4 +1,4 @@ +-b ++a + ' . ' + ' . ' + ' . ' +', + "b\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + "a\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + ], + 'diff line @1' => [ +'--- Original ++++ New +@@ -1,2 +1,2 @@ + ' . ' +-AG ++B +', + "\nAG\n", + "\nB\n", + ], + 'same multiple lines' => [ +'--- Original ++++ New +@@ -1,4 +1,4 @@ + ' . ' + ' . ' +-V ++B + C213 +', + "\n\nV\nC213", + "\n\nB\nC213", + ], + 'diff last line I' => [ +'--- Original ++++ New +@@ -5,4 +5,4 @@ + ' . ' + ' . ' + ' . ' +-E ++B +', + "A\n\n\n\n\n\n\nE\n", + "A\n\n\n\n\n\n\nB\n", + ], + 'diff line middle' => [ +'--- Original ++++ New +@@ -5,7 +5,7 @@ + ' . ' + ' . ' + ' . ' +-X ++Z + ' . ' + ' . ' + ' . ' +', + "A\n\n\n\n\n\n\nX\n\n\n\n\n\n\nAY", + "A\n\n\n\n\n\n\nZ\n\n\n\n\n\n\nAY", + ], + 'diff last line III' => [ +'--- Original ++++ New +@@ -12,4 +12,4 @@ + ' . ' + ' . ' + ' . ' +-A ++B +', + "A\n\n\n\n\n\n\nA\n\n\n\n\n\n\nA\n", + "A\n\n\n\n\n\n\nA\n\n\n\n\n\n\nB\n", + ], + [ +'--- Original ++++ New +@@ -1,8 +1,8 @@ + A +-B ++B1 + D + E + EE + F +-G ++G1 + H +', + "A\nB\nD\nE\nEE\nF\nG\nH", + "A\nB1\nD\nE\nEE\nF\nG1\nH", + ], + [ +'--- Original ++++ New +@@ -1,4 +1,5 @@ + Z ++ + a + b + c +@@ -7,5 +8,5 @@ + f + g + h +-i ++x + j +', +'Z +a +b +c +d +e +f +g +h +i +j +', +'Z + +a +b +c +d +e +f +g +h +x +j +', + ], + [ +'--- Original ++++ New +@@ -1,7 +1,5 @@ +- +-a ++b + A +-X +- ++Y + ' . ' + A +', + "\na\nA\nX\n\n\nA\n", + "b\nA\nY\n\nA\n", + ], + [ +<< + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0\Output; + +use PHPUnit\Framework\TestCase; +use PhpCsFixer\Diff\v3_0\Differ; + +/** + * @covers PhpCsFixer\Diff\v3_0\Output\UnifiedDiffOutputBuilder + * + * @uses PhpCsFixer\Diff\v3_0\Differ + * @uses PhpCsFixer\Diff\v3_0\Output\AbstractChunkOutputBuilder + * @uses PhpCsFixer\Diff\v3_0\TimeEfficientLongestCommonSubsequenceCalculator + */ +final class UnifiedDiffOutputBuilderTest extends TestCase +{ + /** + * @param string $expected + * @param string $from + * @param string $to + * @param string $header + * + * @dataProvider headerProvider + */ + public function testCustomHeaderCanBeUsed($expected, $from, $to, $header) + { + $differ = new Differ(new UnifiedDiffOutputBuilder($header)); + + $this->assertSame( + $expected, + $differ->diff($from, $to) + ); + } + + public function headerProvider() + { + return [ + [ + "CUSTOM HEADER\n@@ @@\n-a\n+b\n", + 'a', + 'b', + 'CUSTOM HEADER', + ], + [ + "CUSTOM HEADER\n@@ @@\n-a\n+b\n", + 'a', + 'b', + "CUSTOM HEADER\n", + ], + [ + "CUSTOM HEADER\n\n@@ @@\n-a\n+b\n", + 'a', + 'b', + "CUSTOM HEADER\n\n", + ], + [ + "@@ @@\n-a\n+b\n", + 'a', + 'b', + '', + ], + ]; + } + + /** + * @param string $expected + * @param string $from + * @param string $to + * + * @dataProvider provideDiffWithLineNumbers + */ + public function testDiffWithLineNumbers($expected, $from, $to) + { + $differ = new Differ(new UnifiedDiffOutputBuilder("--- Original\n+++ New\n", true)); + $this->assertSame($expected, $differ->diff($from, $to)); + } + + public function provideDiffWithLineNumbers() + { + return UnifiedDiffOutputBuilderDataProvider::provideDiffWithLineNumbers(); + } +} diff --git a/tests/v3_0/ParserTest.php b/tests/v3_0/ParserTest.php new file mode 100644 index 00000000..53f3a3ac --- /dev/null +++ b/tests/v3_0/ParserTest.php @@ -0,0 +1,152 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0; + +use PHPUnit\Framework\TestCase; +use PhpCsFixer\Diff\v3_0\Utils\FileUtils; + +/** + * @covers PhpCsFixer\Diff\v3_0\Parser + * + * @uses PhpCsFixer\Diff\v3_0\Chunk + * @uses PhpCsFixer\Diff\v3_0\Diff + * @uses PhpCsFixer\Diff\v3_0\Line + */ +final class ParserTest extends TestCase +{ + /** + * @var Parser + */ + private $parser; + + protected function setUp() + { + $this->parser = new Parser; + } + + public function testParse() + { + $content = FileUtils::getFileContent(__DIR__ . '/fixtures/patch.txt'); + + $diffs = $this->parser->parse($content); + + $this->assertInternalType('array', $diffs); + $this->assertContainsOnlyInstancesOf(Diff::class, $diffs); + $this->assertCount(1, $diffs); + + $chunks = $diffs[0]->getChunks(); + $this->assertInternalType('array', $chunks); + $this->assertContainsOnlyInstancesOf(Chunk::class, $chunks); + + $this->assertCount(1, $chunks); + + $this->assertSame(20, $chunks[0]->getStart()); + + $this->assertCount(4, $chunks[0]->getLines()); + } + + public function testParseWithMultipleChunks() + { + $content = FileUtils::getFileContent(__DIR__ . '/fixtures/patch2.txt'); + + $diffs = $this->parser->parse($content); + + $this->assertCount(1, $diffs); + + $chunks = $diffs[0]->getChunks(); + $this->assertCount(3, $chunks); + + $this->assertSame(20, $chunks[0]->getStart()); + $this->assertSame(320, $chunks[1]->getStart()); + $this->assertSame(600, $chunks[2]->getStart()); + + $this->assertCount(5, $chunks[0]->getLines()); + $this->assertCount(5, $chunks[1]->getLines()); + $this->assertCount(4, $chunks[2]->getLines()); + } + + public function testParseWithRemovedLines() + { + $content = <<parser->parse($content); + $this->assertInternalType('array', $diffs); + $this->assertContainsOnlyInstancesOf(Diff::class, $diffs); + $this->assertCount(1, $diffs); + + $chunks = $diffs[0]->getChunks(); + + $this->assertInternalType('array', $chunks); + $this->assertContainsOnlyInstancesOf(Chunk::class, $chunks); + $this->assertCount(1, $chunks); + + $chunk = $chunks[0]; + $this->assertSame(49, $chunk->getStart()); + $this->assertSame(49, $chunk->getEnd()); + $this->assertSame(9, $chunk->getStartRange()); + $this->assertSame(8, $chunk->getEndRange()); + + $lines = $chunk->getLines(); + $this->assertInternalType('array', $lines); + $this->assertContainsOnlyInstancesOf(Line::class, $lines); + $this->assertCount(2, $lines); + + /** @var Line $line */ + $line = $lines[0]; + $this->assertSame('A', $line->getContent()); + $this->assertSame(Line::UNCHANGED, $line->getType()); + + $line = $lines[1]; + $this->assertSame('B', $line->getContent()); + $this->assertSame(Line::REMOVED, $line->getType()); + } + + public function testParseDiffForMulitpleFiles() + { + $content = <<parser->parse($content); + $this->assertCount(2, $diffs); + + /** @var Diff $diff */ + $diff = $diffs[0]; + $this->assertSame('a/Test.txt', $diff->getFrom()); + $this->assertSame('b/Test.txt', $diff->getTo()); + $this->assertCount(1, $diff->getChunks()); + + $diff = $diffs[1]; + $this->assertSame('a/Test2.txt', $diff->getFrom()); + $this->assertSame('b/Test2.txt', $diff->getTo()); + $this->assertCount(1, $diff->getChunks()); + } +} diff --git a/tests/v3_0/TimeEfficientImplementationTest.php b/tests/v3_0/TimeEfficientImplementationTest.php new file mode 100644 index 00000000..a68c6cc1 --- /dev/null +++ b/tests/v3_0/TimeEfficientImplementationTest.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0; + +/** + * @covers PhpCsFixer\Diff\v3_0\TimeEfficientLongestCommonSubsequenceCalculator + */ +final class TimeEfficientImplementationTest extends LongestCommonSubsequenceTest +{ + protected function createImplementation() + { + return new TimeEfficientLongestCommonSubsequenceCalculator; + } +} diff --git a/tests/v3_0/Utils/FileUtils.php b/tests/v3_0/Utils/FileUtils.php new file mode 100644 index 00000000..4ce4edd1 --- /dev/null +++ b/tests/v3_0/Utils/FileUtils.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0\Utils; + +final class FileUtils +{ + public static function getFileContent($file) + { + $content = @\file_get_contents($file); + if (false === $content) { + $error = \error_get_last(); + + throw new \RuntimeException(\sprintf( + 'Failed to read content of file "%s".%s', + $file, + $error ? ' ' . $error['message'] : '' + )); + } + + return $content; + } +} diff --git a/tests/v3_0/Utils/UnifiedDiffAssertTrait.php b/tests/v3_0/Utils/UnifiedDiffAssertTrait.php new file mode 100644 index 00000000..cc3ca641 --- /dev/null +++ b/tests/v3_0/Utils/UnifiedDiffAssertTrait.php @@ -0,0 +1,274 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0\Utils; + +trait UnifiedDiffAssertTrait +{ + /** + * @param string $diff + * + * @throws \UnexpectedValueException + */ + public function assertValidUnifiedDiffFormat($diff) + { + if ('' === $diff) { + $this->addToAssertionCount(1); + + return; + } + + // test diff ends with a line break + $last = \substr($diff, -1); + if ("\n" !== $last && "\r" !== $last) { + throw new \UnexpectedValueException(\sprintf('Expected diff to end with a line break, got "%s".', $last)); + } + + $lines = \preg_split('/(.*\R)/', $diff, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + $lineCount = \count($lines); + $lineNumber = $diffLineFromNumber = $diffLineToNumber = 1; + $fromStart = $fromTillOffset = $toStart = $toTillOffset = -1; + $expectHunkHeader = true; + + // check for header + if ($lineCount > 1) { + $this->unifiedDiffAssertLinePrefix($lines[0], 'Line 1.'); + $this->unifiedDiffAssertLinePrefix($lines[1], 'Line 2.'); + + if ('---' === \substr($lines[0], 0, 3)) { + if ('+++' !== \substr($lines[1], 0, 3)) { + throw new \UnexpectedValueException(\sprintf("Line 1 indicates a header, so line 2 must start with \"+++\".\nLine 1: \"%s\"\nLine 2: \"%s\".", $lines[0], $lines[1])); + } + + $this->unifiedDiffAssertHeaderLine($lines[0], '--- ', 'Line 1.'); + $this->unifiedDiffAssertHeaderLine($lines[1], '+++ ', 'Line 2.'); + + $lineNumber = 3; + } + } + + $endOfLineTypes = []; + $diffClosed = false; + + // assert format of lines, get all hunks, test the line numbers + for (; $lineNumber <= $lineCount; ++$lineNumber) { + if ($diffClosed) { + throw new \UnexpectedValueException(\sprintf('Unexpected line as 2 "No newline" markers have found, ". Line %d.', $lineNumber)); + } + + $line = $lines[$lineNumber - 1]; // line numbers start by 1, array index at 0 + $type = $this->unifiedDiffAssertLinePrefix($line, \sprintf('Line %d.', $lineNumber)); + + if ($expectHunkHeader && '@' !== $type && '\\' !== $type) { + throw new \UnexpectedValueException(\sprintf('Expected hunk start (\'@\'), got "%s". Line %d.', $type, $lineNumber)); + } + + if ('@' === $type) { + if (!$expectHunkHeader) { + throw new \UnexpectedValueException(\sprintf('Unexpected hunk start (\'@\'). Line %d.', $lineNumber)); + } + + $previousHunkFromEnd = $fromStart + $fromTillOffset; + $previousHunkTillEnd = $toStart + $toTillOffset; + + list($fromStart, $fromTillOffset, $toStart, $toTillOffset) = $this->unifiedDiffAssertHunkHeader($line, \sprintf('Line %d.', $lineNumber)); + + // detect overlapping hunks + if ($fromStart < $previousHunkFromEnd) { + throw new \UnexpectedValueException(\sprintf('Unexpected new hunk; "from" (\'-\') start overlaps previous hunk. Line %d.', $lineNumber)); + } + + if ($toStart < $previousHunkTillEnd) { + throw new \UnexpectedValueException(\sprintf('Unexpected new hunk; "to" (\'+\') start overlaps previous hunk. Line %d.', $lineNumber)); + } + + /* valid states; hunks touches against each other: + $fromStart === $previousHunkFromEnd + $toStart === $previousHunkTillEnd + */ + + $diffLineFromNumber = $fromStart; + $diffLineToNumber = $toStart; + $expectHunkHeader = false; + + continue; + } + + if ('-' === $type) { + if (isset($endOfLineTypes['-'])) { + throw new \UnexpectedValueException(\sprintf('Not expected from (\'-\'), already closed by "\\ No newline at end of file". Line %d.', $lineNumber)); + } + + ++$diffLineFromNumber; + } elseif ('+' === $type) { + if (isset($endOfLineTypes['+'])) { + throw new \UnexpectedValueException(\sprintf('Not expected to (\'+\'), already closed by "\\ No newline at end of file". Line %d.', $lineNumber)); + } + + ++$diffLineToNumber; + } elseif (' ' === $type) { + if (isset($endOfLineTypes['-'])) { + throw new \UnexpectedValueException(\sprintf('Not expected same (\' \'), \'-\' already closed by "\\ No newline at end of file". Line %d.', $lineNumber)); + } + + if (isset($endOfLineTypes['+'])) { + throw new \UnexpectedValueException(\sprintf('Not expected same (\' \'), \'+\' already closed by "\\ No newline at end of file". Line %d.', $lineNumber)); + } + + ++$diffLineFromNumber; + ++$diffLineToNumber; + } elseif ('\\' === $type) { + if (!isset($lines[$lineNumber - 2])) { + throw new \UnexpectedValueException(\sprintf('Unexpected "\\ No newline at end of file", it must be preceded by \'+\' or \'-\' line. Line %d.', $lineNumber)); + } + + $previousType = $this->unifiedDiffAssertLinePrefix($lines[$lineNumber - 2], \sprintf('Preceding line of "\\ No newline at end of file" of unexpected format. Line %d.', $lineNumber)); + if (isset($endOfLineTypes[$previousType])) { + throw new \UnexpectedValueException(\sprintf('Unexpected "\\ No newline at end of file", "%s" was already closed. Line %d.', $type, $lineNumber)); + } + + $endOfLineTypes[$previousType] = true; + $diffClosed = \count($endOfLineTypes) > 1; + } else { + // internal state error + throw new \RuntimeException(\sprintf('Unexpected line type "%s" Line %d.', $type, $lineNumber)); + } + + $expectHunkHeader = + $diffLineFromNumber === ($fromStart + $fromTillOffset) + && $diffLineToNumber === ($toStart + $toTillOffset) + ; + } + + if ( + $diffLineFromNumber !== ($fromStart + $fromTillOffset) + && $diffLineToNumber !== ($toStart + $toTillOffset) + ) { + throw new \UnexpectedValueException(\sprintf('Unexpected EOF, number of lines in hunk "from" (\'-\')) and "to" (\'+\') mismatched. Line %d.', $lineNumber)); + } + + if ($diffLineFromNumber !== ($fromStart + $fromTillOffset)) { + throw new \UnexpectedValueException(\sprintf('Unexpected EOF, number of lines in hunk "from" (\'-\')) mismatched. Line %d.', $lineNumber)); + } + + if ($diffLineToNumber !== ($toStart + $toTillOffset)) { + throw new \UnexpectedValueException(\sprintf('Unexpected EOF, number of lines in hunk "to" (\'+\')) mismatched. Line %d.', $lineNumber)); + } + + $this->addToAssertionCount(1); + } + + /** + * @param string $line + * @param string $message + * + * @return string '+', '-', '@', ' ' or '\' + */ + private function unifiedDiffAssertLinePrefix($line, $message) + { + $this->unifiedDiffAssertStrLength($line, 2, $message); // 2: line type indicator ('+', '-', ' ' or '\') and a line break + $firstChar = $line[0]; + + if ('+' === $firstChar || '-' === $firstChar || '@' === $firstChar || ' ' === $firstChar) { + return $firstChar; + } + + if ("\\ No newline at end of file\n" === $line) { + return '\\'; + } + + throw new \UnexpectedValueException(\sprintf('Expected line to start with \'@\', \'-\' or \'+\', got "%s". %s', $line, $message)); + } + + private function unifiedDiffAssertStrLength($line, $min, $message) + { + $length = \strlen($line); + if ($length < $min) { + throw new \UnexpectedValueException(\sprintf('Expected string length of minimal %d, got %d. %s', $min, $length, $message)); + } + } + + /** + * Assert valid unified diff header line + * + * Samples: + * - "+++ from1.txt\t2017-08-24 19:51:29.383985722 +0200" + * - "+++ from1.txt" + * + * @param string $line + * @param string $start + * @param string $message + */ + private function unifiedDiffAssertHeaderLine($line, $start, $message) + { + if (0 !== \strpos($line, $start)) { + throw new \UnexpectedValueException(\sprintf('Expected header line to start with "%s", got "%s". %s', $start . ' ', $line, $message)); + } + + // sample "+++ from1.txt\t2017-08-24 19:51:29.383985722 +0200\n" + $match = \preg_match( + "/^([^\t]*)(?:[\t]([\\S].*[\\S]))?\n$/", + \substr($line, 4), // 4 === string length of "+++ " / "--- " + $matches + ); + + if (1 !== $match) { + throw new \UnexpectedValueException(\sprintf('Header line does not match expected pattern, got "%s". %s', $line, $message)); + } + + // $file = $matches[1]; + + if (\count($matches) > 2) { + $this->unifiedDiffAssertHeaderDate($matches[2], $message); + } + } + + private function unifiedDiffAssertHeaderDate($date, $message) + { + // sample "2017-08-24 19:51:29.383985722 +0200" + $match = \preg_match( + '/^([\d]{4})-([01]?[\d])-([0123]?[\d])(:? [\d]{1,2}:[\d]{1,2}(?::[\d]{1,2}(:?\.[\d]+)?)?(?: ([\+\-][\d]{4}))?)?$/', + $date, + $matches + ); + + if (1 !== $match || ($matchesCount = \count($matches)) < 4) { + throw new \UnexpectedValueException(\sprintf('Date of header line does not match expected pattern, got "%s". %s', $date, $message)); + } + + // [$full, $year, $month, $day, $time] = $matches; + } + + /** + * @param string $line + * @param string $message + * + * @return int[] + */ + private function unifiedDiffAssertHunkHeader($line, $message) + { + if (1 !== \preg_match('#^@@ -([\d]+)((?:,[\d]+)?) \+([\d]+)((?:,[\d]+)?) @@\n$#', $line, $matches)) { + throw new \UnexpectedValueException( + \sprintf( + 'Hunk header line does not match expected pattern, got "%s". %s', + $line, + $message + ) + ); + } + + return [ + (int) $matches[1], + empty($matches[2]) ? 1 : (int) \substr($matches[2], 1), + (int) $matches[3], + empty($matches[4]) ? 1 : (int) \substr($matches[4], 1), + ]; + } +} diff --git a/tests/v3_0/Utils/UnifiedDiffAssertTraitIntegrationTest.php b/tests/v3_0/Utils/UnifiedDiffAssertTraitIntegrationTest.php new file mode 100644 index 00000000..d49a206b --- /dev/null +++ b/tests/v3_0/Utils/UnifiedDiffAssertTraitIntegrationTest.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0\Utils; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Process\Process; + +/** + * @requires OS Linux + * + * @coversNothing + */ +final class UnifiedDiffAssertTraitIntegrationTest extends TestCase +{ + use UnifiedDiffAssertTrait; + + private $filePatch; + + protected function setUp() + { + $this->filePatch = __DIR__ . '/../fixtures/out/patch.txt'; + + $this->cleanUpTempFiles(); + } + + protected function tearDown() + { + $this->cleanUpTempFiles(); + } + + /** + * @param string $fileFrom + * @param string $fileTo + * + * @dataProvider provideFilePairsCases + */ + public function testValidPatches($fileFrom, $fileTo) + { + $command = \sprintf( + 'diff -u %s %s > %s', + \escapeshellarg(\realpath($fileFrom)), + \escapeshellarg(\realpath($fileTo)), + \escapeshellarg($this->filePatch) + ); + + $p = new Process($command); + $p->run(); + + $exitCode = $p->getExitCode(); + + if (0 === $exitCode) { + // odd case when two files have the same content. Test after executing as it is more efficient than to read the files and check the contents every time. + $this->addToAssertionCount(1); + + return; + } + + $this->assertSame( + 1, // means `diff` found a diff between the files we gave it + $exitCode, + \sprintf( + "Command exec. was not successful:\n\"%s\"\nOutput:\n\"%s\"\nStdErr:\n\"%s\"\nExit code %d.\n", + $command, + $p->getOutput(), + $p->getErrorOutput(), + $p->getExitCode() + ) + ); + + $this->assertValidUnifiedDiffFormat(FileUtils::getFileContent($this->filePatch)); + } + + /** + * @return array> + */ + public function provideFilePairsCases() + { + $cases = []; + + // created cases based on dedicated fixtures + $dir = \realpath(__DIR__ . '/../fixtures/UnifiedDiffAssertTraitIntegrationTest'); + $dirLength = \strlen($dir); + + for ($i = 1;; ++$i) { + $fromFile = \sprintf('%s/%d_a.txt', $dir, $i); + $toFile = \sprintf('%s/%d_b.txt', $dir, $i); + + if (!\file_exists($fromFile)) { + break; + } + + $this->assertFileExists($toFile); + $cases[\sprintf("Diff file:\n\"%s\"\nvs.\n\"%s\"\n", \substr(\realpath($fromFile), $dirLength), \substr(\realpath($toFile), $dirLength))] = [$fromFile, $toFile]; + } + + // create cases based on PHP files within the vendor directory for integration testing + $dir = \realpath(__DIR__ . '/../../../vendor'); + $dirLength = \strlen($dir); + + $fileIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS)); + $fromFile = __FILE__; + + /** @var \SplFileInfo $file */ + foreach ($fileIterator as $file) { + if ('php' !== $file->getExtension()) { + continue; + } + + $toFile = $file->getPathname(); + $cases[\sprintf("Diff file:\n\"%s\"\nvs.\n\"%s\"\n", \substr(\realpath($fromFile), $dirLength), \substr(\realpath($toFile), $dirLength))] = [$fromFile, $toFile]; + $fromFile = $toFile; + } + + return $cases; + } + + private function cleanUpTempFiles() + { + @\unlink($this->filePatch); + } +} diff --git a/tests/v3_0/Utils/UnifiedDiffAssertTraitTest.php b/tests/v3_0/Utils/UnifiedDiffAssertTraitTest.php new file mode 100644 index 00000000..882e1f26 --- /dev/null +++ b/tests/v3_0/Utils/UnifiedDiffAssertTraitTest.php @@ -0,0 +1,434 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpCsFixer\Diff\v3_0\Utils; + +use PHPUnit\Framework\TestCase; + +/** + * @covers PhpCsFixer\Diff\v3_0\Utils\UnifiedDiffAssertTrait + */ +final class UnifiedDiffAssertTraitTest extends TestCase +{ + use UnifiedDiffAssertTrait; + + /** + * @param string $diff + * + * @dataProvider provideValidCases + */ + public function testValidCases($diff) + { + $this->assertValidUnifiedDiffFormat($diff); + } + + public function provideValidCases() + { + return [ + [ +'--- Original ++++ New +@@ -8 +8 @@ +-Z ++U +', + ], + [ +'--- Original ++++ New +@@ -8 +8 @@ +-Z ++U +@@ -15 +15 @@ +-X ++V +', + ], + 'empty diff. is valid' => [ + '', + ], + ]; + } + + public function testNoLinebreakEnd() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Expected diff to end with a line break, got "C".', '#'))); + + $this->assertValidUnifiedDiffFormat("A\nB\nC"); + } + + public function testInvalidStartWithoutHeader() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote("Expected line to start with '@', '-' or '+', got \"A\n\". Line 1.", '#'))); + + $this->assertValidUnifiedDiffFormat("A\n"); + } + + public function testInvalidStartHeader1() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote("Line 1 indicates a header, so line 2 must start with \"+++\".\nLine 1: \"--- A\n\"\nLine 2: \"+ 1\n\".", '#'))); + + $this->assertValidUnifiedDiffFormat("--- A\n+ 1\n"); + } + + public function testInvalidStartHeader2() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote("Header line does not match expected pattern, got \"+++ file X\n\". Line 2.", '#'))); + + $this->assertValidUnifiedDiffFormat("--- A\n+++ file\tX\n"); + } + + public function testInvalidStartHeader3() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Date of header line does not match expected pattern, got "[invalid date]". Line 1.', '#'))); + + $this->assertValidUnifiedDiffFormat( +"--- Original\t[invalid date] ++++ New +@@ -1,2 +1,2 @@ +-A ++B + " . ' +' + ); + } + + public function testInvalidStartHeader4() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote("Expected header line to start with \"+++ \", got \"+++INVALID\n\". Line 2.", '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++INVALID +@@ -1,2 +1,2 @@ +-A ++B + ' . ' +' + ); + } + + public function testInvalidLine1() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote("Expected line to start with '@', '-' or '+', got \"1\n\". Line 5.", '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++ New +@@ -8 +8 @@ +-Z +1 ++U +' + ); + } + + public function testInvalidLine2() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Expected string length of minimal 2, got 1. Line 4.', '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++ New +@@ -8 +8 @@ + + +' + ); + } + + public function testHunkInvalidFormat() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote("Hunk header line does not match expected pattern, got \"@@ INVALID -1,1 +1,1 @@\n\". Line 3.", '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++ New +@@ INVALID -1,1 +1,1 @@ +-Z ++U +' + ); + } + + public function testHunkOverlapFrom() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected new hunk; "from" (\'-\') start overlaps previous hunk. Line 6.', '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++ New +@@ -8,1 +8,1 @@ +-Z ++U +@@ -7,1 +9,1 @@ +-Z ++U +' + ); + } + + public function testHunkOverlapTo() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected new hunk; "to" (\'+\') start overlaps previous hunk. Line 6.', '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++ New +@@ -8,1 +8,1 @@ +-Z ++U +@@ -17,1 +7,1 @@ +-Z ++U +' + ); + } + + public function testExpectHunk1() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Expected hunk start (\'@\'), got "+". Line 6.', '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++ New +@@ -8 +8 @@ +-Z ++U ++O +' + ); + } + + public function testExpectHunk2() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected hunk start (\'@\'). Line 6.', '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++ New +@@ -8,12 +8,12 @@ + ' . ' + ' . ' +@@ -38,12 +48,12 @@ +' + ); + } + + public function testMisplacedLineAfterComments1() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected line as 2 "No newline" markers have found, ". Line 8.', '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++ New +@@ -8 +8 @@ +-Z +\ No newline at end of file ++U +\ No newline at end of file ++A +' + ); + } + + public function testMisplacedLineAfterComments2() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected line as 2 "No newline" markers have found, ". Line 7.', '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++ New +@@ -8 +8 @@ ++U +\ No newline at end of file +\ No newline at end of file +\ No newline at end of file +' + ); + } + + public function testMisplacedLineAfterComments3() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected line as 2 "No newline" markers have found, ". Line 7.', '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++ New +@@ -8 +8 @@ ++U +\ No newline at end of file +\ No newline at end of file ++A +' + ); + } + + public function testMisplacedComment() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected "\ No newline at end of file", it must be preceded by \'+\' or \'-\' line. Line 1.', '#'))); + + $this->assertValidUnifiedDiffFormat( +'\ No newline at end of file +' + ); + } + + public function testUnexpectedDuplicateNoNewLineEOF() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected "\\ No newline at end of file", "\\" was already closed. Line 8.', '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++ New +@@ -8,12 +8,12 @@ + ' . ' + ' . ' +\ No newline at end of file + ' . ' +\ No newline at end of file +' + ); + } + + public function testFromAfterClose() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Not expected from (\'-\'), already closed by "\ No newline at end of file". Line 6.', '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++ New +@@ -8,12 +8,12 @@ +-A +\ No newline at end of file +-A +\ No newline at end of file +' + ); + } + + public function testSameAfterFromClose() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Not expected same (\' \'), \'-\' already closed by "\ No newline at end of file". Line 6.', '#'))); + + $this->assertValidUnifiedDiffFormat( + '--- Original ++++ New +@@ -8,12 +8,12 @@ +-A +\ No newline at end of file + A +\ No newline at end of file +' + ); + } + + public function testToAfterClose() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Not expected to (\'+\'), already closed by "\ No newline at end of file". Line 6.', '#'))); + + $this->assertValidUnifiedDiffFormat( + '--- Original ++++ New +@@ -8,12 +8,12 @@ ++A +\ No newline at end of file ++A +\ No newline at end of file +' + ); + } + + public function testSameAfterToClose() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Not expected same (\' \'), \'+\' already closed by "\ No newline at end of file". Line 6.', '#'))); + + $this->assertValidUnifiedDiffFormat( + '--- Original ++++ New +@@ -8,12 +8,12 @@ ++A +\ No newline at end of file + A +\ No newline at end of file +' + ); + } + + public function testUnexpectedEOFFromMissingLines() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected EOF, number of lines in hunk "from" (\'-\')) mismatched. Line 7.', '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++ New +@@ -8,19 +7,2 @@ +-A ++B + ' . ' +' + ); + } + + public function testUnexpectedEOFToMissingLines() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected EOF, number of lines in hunk "to" (\'+\')) mismatched. Line 7.', '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++ New +@@ -8,2 +7,3 @@ +-A ++B + ' . ' +' + ); + } + + public function testUnexpectedEOFBothFromAndToMissingLines() + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected EOF, number of lines in hunk "from" (\'-\')) and "to" (\'+\') mismatched. Line 7.', '#'))); + + $this->assertValidUnifiedDiffFormat( +'--- Original ++++ New +@@ -1,12 +1,14 @@ +-A ++B + ' . ' +' + ); + } +} diff --git a/tests/v3_0/fixtures/.editorconfig b/tests/v3_0/fixtures/.editorconfig new file mode 100644 index 00000000..78b36ca0 --- /dev/null +++ b/tests/v3_0/fixtures/.editorconfig @@ -0,0 +1 @@ +root = true diff --git a/tests/v3_0/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_a.txt b/tests/v3_0/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_a.txt new file mode 100644 index 00000000..2e65efe2 --- /dev/null +++ b/tests/v3_0/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_a.txt @@ -0,0 +1 @@ +a \ No newline at end of file diff --git a/tests/v3_0/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_b.txt b/tests/v3_0/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_b.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/v3_0/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_a.txt b/tests/v3_0/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_a.txt new file mode 100644 index 00000000..c7fe26e9 --- /dev/null +++ b/tests/v3_0/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_a.txt @@ -0,0 +1,35 @@ +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a \ No newline at end of file diff --git a/tests/v3_0/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_b.txt b/tests/v3_0/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_b.txt new file mode 100644 index 00000000..377a70f8 --- /dev/null +++ b/tests/v3_0/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_b.txt @@ -0,0 +1,18 @@ +a +a +a +a +a +a +a +a +a +a +b +a +a +a +a +a +a +c \ No newline at end of file diff --git a/tests/v3_0/fixtures/out/.editorconfig b/tests/v3_0/fixtures/out/.editorconfig new file mode 100644 index 00000000..78b36ca0 --- /dev/null +++ b/tests/v3_0/fixtures/out/.editorconfig @@ -0,0 +1 @@ +root = true diff --git a/tests/v3_0/fixtures/out/.gitignore b/tests/v3_0/fixtures/out/.gitignore new file mode 100644 index 00000000..f6f7a478 --- /dev/null +++ b/tests/v3_0/fixtures/out/.gitignore @@ -0,0 +1,2 @@ +# reset all ignore rules to create sandbox for integration test +!/** \ No newline at end of file diff --git a/tests/v3_0/fixtures/patch.txt b/tests/v3_0/fixtures/patch.txt new file mode 100644 index 00000000..144b61d0 --- /dev/null +++ b/tests/v3_0/fixtures/patch.txt @@ -0,0 +1,9 @@ +diff --git a/Foo.php b/Foo.php +index abcdefg..abcdefh 100644 +--- a/Foo.php ++++ b/Foo.php +@@ -20,4 +20,5 @@ class Foo + const ONE = 1; + const TWO = 2; ++ const THREE = 3; + const FOUR = 4; diff --git a/tests/v3_0/fixtures/patch2.txt b/tests/v3_0/fixtures/patch2.txt new file mode 100644 index 00000000..41fbc959 --- /dev/null +++ b/tests/v3_0/fixtures/patch2.txt @@ -0,0 +1,21 @@ +diff --git a/Foo.php b/Foo.php +index abcdefg..abcdefh 100644 +--- a/Foo.php ++++ b/Foo.php +@@ -20,4 +20,5 @@ class Foo + const ONE = 1; + const TWO = 2; ++ const THREE = 3; + const FOUR = 4; + +@@ -320,4 +320,5 @@ class Foo + const A = 'A'; + const B = 'B'; ++ const C = 'C'; + const D = 'D'; + +@@ -600,4 +600,5 @@ class Foo + public function doSomething() { + ++ return 'foo'; + } From 13254938cb6e0730393e3b8cd7b0f550d6e52e94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Rumi=C5=84ski?= Date: Tue, 27 Mar 2018 08:52:11 +0200 Subject: [PATCH 14/25] Backport only new Diff v3 (#9) --- LICENSE_GECKO | 19 - README.md | 4 - composer.json | 12 +- src/{v3_0 => }/Chunk.php | 2 +- src/{v3_0 => }/Diff.php | 2 +- src/{v3_0 => }/Differ.php | 6 +- .../Exception/ConfigurationException.php | 2 +- src/{v2_0 => }/Exception/Exception.php | 2 +- .../Exception/InvalidArgumentException.php | 2 +- .../ConfigurationException.php | 36 - .../UnifiedDiffOutputBuilder.php | 295 ----- src/{v3_0 => }/Line.php | 2 +- .../LongestCommonSubsequenceCalculator.php | 2 +- ...ientLongestCommonSubsequenceCalculator.php | 2 +- .../Output/AbstractChunkOutputBuilder.php | 2 +- .../Output/DiffOnlyOutputBuilder.php | 4 +- .../Output/DiffOutputBuilderInterface.php | 2 +- .../Output/StrictUnifiedDiffOutputBuilder.php | 6 +- .../Output/UnifiedDiffOutputBuilder.php | 4 +- src/{v3_0 => }/Parser.php | 2 +- ...ientLongestCommonSubsequenceCalculator.php | 2 +- src/v1_4/Chunk.php | 103 -- src/v1_4/Diff.php | 73 -- src/v1_4/Differ.php | 399 ------- src/v1_4/LCS/LongestCommonSubsequence.php | 27 - ...LongestCommonSubsequenceImplementation.php | 95 -- ...LongestCommonSubsequenceImplementation.php | 74 -- src/v1_4/Line.php | 54 - src/v1_4/Parser.php | 110 -- src/v2_0/Chunk.php | 78 -- src/v2_0/Diff.php | 67 -- src/v2_0/Differ.php | 321 ------ .../Exception/InvalidArgumentException.php | 15 - src/v2_0/Line.php | 44 - .../LongestCommonSubsequenceCalculator.php | 24 - ...ientLongestCommonSubsequenceCalculator.php | 81 -- src/v2_0/Output/DiffOnlyOutputBuilder.php | 63 - .../Output/DiffOutputBuilderInterface.php | 19 - src/v2_0/Output/UnifiedDiffOutputBuilder.php | 165 --- src/v2_0/Parser.php | 106 -- ...ientLongestCommonSubsequenceCalculator.php | 66 -- src/v3_0/Exception/Exception.php | 15 - .../Output/AbstractChunkOutputBuilder.php | 56 - tests/{v3_0 => }/ChunkTest.php | 4 +- tests/{v3_0 => }/DiffTest.php | 6 +- tests/{v3_0 => }/DifferTest.php | 14 +- .../Exception/ConfigurationExceptionTest.php | 4 +- .../InvalidArgumentExceptionTest.php | 4 +- .../Tests/AbstractDiffOutputBuilderTest.php | 97 -- .../Tests/ConfigurationExceptionTest.php | 32 - .../UnifiedDiffAssertTraitIntegrationTest.php | 135 --- ...nifiedDiffOutputBuilderIntegrationTest.php | 247 ---- .../Tests/UnifiedDiffAssertTraitTest.php | 419 ------- .../UnifiedDiffOutputBuilderDataProvider.php | 197 ---- .../Tests/UnifiedDiffOutputBuilderTest.php | 386 ------- .../Utils/PHPUnitPolyfill.php | 49 - .../Utils/UnifiedDiffAssertTrait.php | 279 ----- tests/{v3_0 => }/LineTest.php | 4 +- .../LongestCommonSubsequenceTest.php | 2 +- .../MemoryEfficientImplementationTest.php | 4 +- .../Output/AbstractChunkOutputBuilderTest.php | 12 +- .../Output/DiffOnlyOutputBuilderTest.php | 10 +- ...nifiedDiffOutputBuilderIntegrationTest.php | 16 +- ...nifiedDiffOutputBuilderIntegrationTest.php | 10 +- ...ctUnifiedDiffOutputBuilderDataProvider.php | 2 +- .../StrictUnifiedDiffOutputBuilderTest.php | 12 +- .../UnifiedDiffOutputBuilderDataProvider.php | 2 +- .../Output/UnifiedDiffOutputBuilderTest.php | 12 +- tests/{v3_0 => }/ParserTest.php | 12 +- .../TimeEfficientImplementationTest.php | 4 +- tests/{v3_0 => }/Utils/FileUtils.php | 2 +- .../Utils/UnifiedDiffAssertTrait.php | 2 +- .../UnifiedDiffAssertTraitIntegrationTest.php | 4 +- .../Utils/UnifiedDiffAssertTraitTest.php | 4 +- .../Integration => }/fixtures/.editorconfig | 0 .../1_a.txt | 0 .../1_b.txt | 0 .../2_a.txt | 0 .../2_b.txt | 0 .../out/.editorconfig | 0 .../Integration => fixtures}/out/.gitignore | 0 tests/{v1_4 => }/fixtures/patch.txt | 0 tests/{v1_4 => }/fixtures/patch2.txt | 0 tests/v1_4/ChunkTest.php | 68 -- tests/v1_4/DiffTest.php | 55 - tests/v1_4/DifferTest.php | 388 ------- .../v1_4/LCS/LongestCommonSubsequenceTest.php | 198 ---- .../LCS/MemoryEfficientImplementationTest.php | 22 - .../LCS/TimeEfficientImplementationTest.php | 22 - tests/v1_4/LineTest.php | 44 - tests/v1_4/ParserTest.php | 151 --- tests/v2_0/ChunkTest.php | 68 -- tests/v2_0/CommonChunksTestOutputBuilder.php | 30 - tests/v2_0/DiffTest.php | 55 - tests/v2_0/DifferTest.php | 1017 ----------------- tests/v2_0/DifferTestTest.php | 83 -- tests/v2_0/LineTest.php | 44 - tests/v2_0/LongestCommonSubsequenceTest.php | 201 ---- .../MemoryEfficientImplementationTest.php | 22 - tests/v2_0/ParserTest.php | 151 --- .../v2_0/TimeEfficientImplementationTest.php | 22 - tests/v2_0/fixtures/patch.txt | 9 - tests/v2_0/fixtures/patch2.txt | 21 - tests/v3_0/fixtures/.editorconfig | 1 - .../1_a.txt | 1 - .../1_b.txt | 0 .../2_a.txt | 35 - .../2_b.txt | 18 - tests/v3_0/fixtures/out/.editorconfig | 1 - tests/v3_0/fixtures/out/.gitignore | 2 - tests/v3_0/fixtures/patch.txt | 9 - tests/v3_0/fixtures/patch2.txt | 21 - 112 files changed, 101 insertions(+), 7110 deletions(-) delete mode 100644 LICENSE_GECKO rename src/{v3_0 => }/Chunk.php (97%) rename src/{v3_0 => }/Diff.php (97%) rename src/{v3_0 => }/Differ.php (98%) rename src/{v3_0 => }/Exception/ConfigurationException.php (96%) rename src/{v2_0 => }/Exception/Exception.php (89%) rename src/{v3_0 => }/Exception/InvalidArgumentException.php (91%) delete mode 100644 src/GeckoPackages/DiffOutputBuilder/ConfigurationException.php delete mode 100644 src/GeckoPackages/DiffOutputBuilder/UnifiedDiffOutputBuilder.php rename src/{v3_0 => }/Line.php (95%) rename src/{v3_0 => }/LongestCommonSubsequenceCalculator.php (94%) rename src/{v3_0 => }/MemoryEfficientLongestCommonSubsequenceCalculator.php (98%) rename src/{v2_0 => }/Output/AbstractChunkOutputBuilder.php (97%) rename src/{v3_0 => }/Output/DiffOnlyOutputBuilder.php (96%) rename src/{v3_0 => }/Output/DiffOutputBuilderInterface.php (91%) rename src/{v3_0 => }/Output/StrictUnifiedDiffOutputBuilder.php (98%) rename src/{v3_0 => }/Output/UnifiedDiffOutputBuilder.php (99%) rename src/{v3_0 => }/Parser.php (98%) rename src/{v3_0 => }/TimeEfficientLongestCommonSubsequenceCalculator.php (98%) delete mode 100644 src/v1_4/Chunk.php delete mode 100644 src/v1_4/Diff.php delete mode 100644 src/v1_4/Differ.php delete mode 100644 src/v1_4/LCS/LongestCommonSubsequence.php delete mode 100644 src/v1_4/LCS/MemoryEfficientLongestCommonSubsequenceImplementation.php delete mode 100644 src/v1_4/LCS/TimeEfficientLongestCommonSubsequenceImplementation.php delete mode 100644 src/v1_4/Line.php delete mode 100644 src/v1_4/Parser.php delete mode 100644 src/v2_0/Chunk.php delete mode 100644 src/v2_0/Diff.php delete mode 100644 src/v2_0/Differ.php delete mode 100644 src/v2_0/Exception/InvalidArgumentException.php delete mode 100644 src/v2_0/Line.php delete mode 100644 src/v2_0/LongestCommonSubsequenceCalculator.php delete mode 100644 src/v2_0/MemoryEfficientLongestCommonSubsequenceCalculator.php delete mode 100644 src/v2_0/Output/DiffOnlyOutputBuilder.php delete mode 100644 src/v2_0/Output/DiffOutputBuilderInterface.php delete mode 100644 src/v2_0/Output/UnifiedDiffOutputBuilder.php delete mode 100644 src/v2_0/Parser.php delete mode 100644 src/v2_0/TimeEfficientLongestCommonSubsequenceCalculator.php delete mode 100644 src/v3_0/Exception/Exception.php delete mode 100644 src/v3_0/Output/AbstractChunkOutputBuilder.php rename tests/{v3_0 => }/ChunkTest.php (95%) rename tests/{v3_0 => }/DiffTest.php (93%) rename tests/{v3_0 => }/DifferTest.php (96%) rename tests/{v3_0 => }/Exception/ConfigurationExceptionTest.php (91%) rename tests/{v3_0 => }/Exception/InvalidArgumentExceptionTest.php (90%) delete mode 100644 tests/GeckoPackages/DiffOutputBuilder/Tests/AbstractDiffOutputBuilderTest.php delete mode 100644 tests/GeckoPackages/DiffOutputBuilder/Tests/ConfigurationExceptionTest.php delete mode 100644 tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/UnifiedDiffAssertTraitIntegrationTest.php delete mode 100644 tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/UnifiedDiffOutputBuilderIntegrationTest.php delete mode 100644 tests/GeckoPackages/DiffOutputBuilder/Tests/UnifiedDiffAssertTraitTest.php delete mode 100644 tests/GeckoPackages/DiffOutputBuilder/Tests/UnifiedDiffOutputBuilderDataProvider.php delete mode 100644 tests/GeckoPackages/DiffOutputBuilder/Tests/UnifiedDiffOutputBuilderTest.php delete mode 100644 tests/GeckoPackages/DiffOutputBuilder/Utils/PHPUnitPolyfill.php delete mode 100644 tests/GeckoPackages/DiffOutputBuilder/Utils/UnifiedDiffAssertTrait.php rename tests/{v3_0 => }/LineTest.php (92%) rename tests/{v3_0 => }/LongestCommonSubsequenceTest.php (99%) rename tests/{v3_0 => }/MemoryEfficientImplementationTest.php (80%) rename tests/{v3_0 => }/Output/AbstractChunkOutputBuilderTest.php (92%) rename tests/{v3_0 => }/Output/DiffOnlyOutputBuilderTest.php (86%) rename tests/{v3_0 => }/Output/Integration/StrictUnifiedDiffOutputBuilderIntegrationTest.php (95%) rename tests/{v3_0 => }/Output/Integration/UnifiedDiffOutputBuilderIntegrationTest.php (94%) rename tests/{v3_0 => }/Output/StrictUnifiedDiffOutputBuilderDataProvider.php (98%) rename tests/{v3_0 => }/Output/StrictUnifiedDiffOutputBuilderTest.php (98%) rename tests/{v3_0 => }/Output/UnifiedDiffOutputBuilderDataProvider.php (99%) rename tests/{v3_0 => }/Output/UnifiedDiffOutputBuilderTest.php (86%) rename tests/{v3_0 => }/ParserTest.php (94%) rename tests/{v3_0 => }/TimeEfficientImplementationTest.php (80%) rename tests/{v3_0 => }/Utils/FileUtils.php (94%) rename tests/{v3_0 => }/Utils/UnifiedDiffAssertTrait.php (99%) rename tests/{v3_0 => }/Utils/UnifiedDiffAssertTraitIntegrationTest.php (97%) rename tests/{v3_0 => }/Utils/UnifiedDiffAssertTraitTest.php (99%) rename tests/{GeckoPackages/DiffOutputBuilder/Tests/Integration => }/fixtures/.editorconfig (100%) rename tests/{GeckoPackages/DiffOutputBuilder/Tests/Integration => }/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_a.txt (100%) rename tests/{GeckoPackages/DiffOutputBuilder/Tests/Integration => }/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_b.txt (100%) rename tests/{GeckoPackages/DiffOutputBuilder/Tests/Integration => }/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_a.txt (100%) rename tests/{GeckoPackages/DiffOutputBuilder/Tests/Integration => }/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_b.txt (100%) rename tests/{GeckoPackages/DiffOutputBuilder/Tests/Integration => fixtures}/out/.editorconfig (100%) rename tests/{GeckoPackages/DiffOutputBuilder/Tests/Integration => fixtures}/out/.gitignore (100%) rename tests/{v1_4 => }/fixtures/patch.txt (100%) rename tests/{v1_4 => }/fixtures/patch2.txt (100%) delete mode 100644 tests/v1_4/ChunkTest.php delete mode 100644 tests/v1_4/DiffTest.php delete mode 100644 tests/v1_4/DifferTest.php delete mode 100644 tests/v1_4/LCS/LongestCommonSubsequenceTest.php delete mode 100644 tests/v1_4/LCS/MemoryEfficientImplementationTest.php delete mode 100644 tests/v1_4/LCS/TimeEfficientImplementationTest.php delete mode 100644 tests/v1_4/LineTest.php delete mode 100644 tests/v1_4/ParserTest.php delete mode 100644 tests/v2_0/ChunkTest.php delete mode 100644 tests/v2_0/CommonChunksTestOutputBuilder.php delete mode 100644 tests/v2_0/DiffTest.php delete mode 100644 tests/v2_0/DifferTest.php delete mode 100644 tests/v2_0/DifferTestTest.php delete mode 100644 tests/v2_0/LineTest.php delete mode 100644 tests/v2_0/LongestCommonSubsequenceTest.php delete mode 100644 tests/v2_0/MemoryEfficientImplementationTest.php delete mode 100644 tests/v2_0/ParserTest.php delete mode 100644 tests/v2_0/TimeEfficientImplementationTest.php delete mode 100644 tests/v2_0/fixtures/patch.txt delete mode 100644 tests/v2_0/fixtures/patch2.txt delete mode 100644 tests/v3_0/fixtures/.editorconfig delete mode 100644 tests/v3_0/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_a.txt delete mode 100644 tests/v3_0/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_b.txt delete mode 100644 tests/v3_0/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_a.txt delete mode 100644 tests/v3_0/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_b.txt delete mode 100644 tests/v3_0/fixtures/out/.editorconfig delete mode 100644 tests/v3_0/fixtures/out/.gitignore delete mode 100644 tests/v3_0/fixtures/patch.txt delete mode 100644 tests/v3_0/fixtures/patch2.txt diff --git a/LICENSE_GECKO b/LICENSE_GECKO deleted file mode 100644 index 066294d5..00000000 --- a/LICENSE_GECKO +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) https://github.com/GeckoPackages - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished -to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index d4106257..f38ce3ac 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,4 @@ Code from `sebastian/diff` has been forked a republished by permission of Sebast Licenced with BSD-3-Clause @ see LICENSE_DIFF, copyright (c) Sebastian Bergmann https://github.com/sebastianbergmann/diff -Code from `GeckoPackages/GeckoDiffOutputBuilder` has been copied and republished by permission of GeckoPackages. -Licenced with MIT @ see LICENSE_GECKO, copyright (c) GeckoPackages https://github.com/GeckoPackages -https://github.com/GeckoPackages/GeckoDiffOutputBuilder/ - For questions visit us @ https://gitter.im/PHP-CS-Fixer/Lobby diff --git a/composer.json b/composer.json index e08be75b..98eed5ea 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "php-cs-fixer/diff", - "description": "sebastian/diff v2 backport support for PHP5.6", + "description": "sebastian/diff v3 backport support for PHP 5.6+", "keywords": ["diff"], "homepage": "https://github.com/PHP-CS-Fixer", "license": "BSD-3-Clause", @@ -24,6 +24,10 @@ "phpunit/phpunit": "^5.7.23 || ^6.4.3", "symfony/process": "^3.3" }, + "config": { + "optimize-autoloader": true, + "sort-packages": true + }, "autoload": { "classmap": [ "src/" @@ -31,11 +35,7 @@ }, "autoload-dev": { "psr-4": { - "PhpCsFixer\\Diff\\v1_4\\Tests\\": "tests/v1_4", - "PhpCsFixer\\Diff\\v2_0\\Tests\\": "tests/v2_0", - "PhpCsFixer\\Diff\\v3_0\\": "tests/v3_0", - "PhpCsFixer\\Diff\\GeckoPackages\\DiffOutputBuilder\\Tests\\": "tests/GeckoPackages/DiffOutputBuilder/Tests", - "PhpCsFixer\\Diff\\GeckoPackages\\DiffOutputBuilder\\Utils\\": "tests/GeckoPackages/DiffOutputBuilder/Utils" + "PhpCsFixer\\Diff\\": "tests" } } } diff --git a/src/v3_0/Chunk.php b/src/Chunk.php similarity index 97% rename from src/v3_0/Chunk.php rename to src/Chunk.php index 6b633c17..317ed867 100644 --- a/src/v3_0/Chunk.php +++ b/src/Chunk.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0; +namespace PhpCsFixer\Diff; final class Chunk { diff --git a/src/v3_0/Diff.php b/src/Diff.php similarity index 97% rename from src/v3_0/Diff.php rename to src/Diff.php index 9f537ec9..a458d274 100644 --- a/src/v3_0/Diff.php +++ b/src/Diff.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0; +namespace PhpCsFixer\Diff; final class Diff { diff --git a/src/v3_0/Differ.php b/src/Differ.php similarity index 98% rename from src/v3_0/Differ.php rename to src/Differ.php index 42315f5a..d252e397 100644 --- a/src/v3_0/Differ.php +++ b/src/Differ.php @@ -8,10 +8,10 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0; +namespace PhpCsFixer\Diff; -use PhpCsFixer\Diff\v3_0\Output\DiffOutputBuilderInterface; -use PhpCsFixer\Diff\v3_0\Output\UnifiedDiffOutputBuilder; +use PhpCsFixer\Diff\Output\DiffOutputBuilderInterface; +use PhpCsFixer\Diff\Output\UnifiedDiffOutputBuilder; /** * Diff implementation. diff --git a/src/v3_0/Exception/ConfigurationException.php b/src/Exception/ConfigurationException.php similarity index 96% rename from src/v3_0/Exception/ConfigurationException.php rename to src/Exception/ConfigurationException.php index 8892d8f9..0cd0f889 100644 --- a/src/v3_0/Exception/ConfigurationException.php +++ b/src/Exception/ConfigurationException.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0; +namespace PhpCsFixer\Diff; final class ConfigurationException extends InvalidArgumentException { diff --git a/src/v2_0/Exception/Exception.php b/src/Exception/Exception.php similarity index 89% rename from src/v2_0/Exception/Exception.php rename to src/Exception/Exception.php index b7e9e926..ba4a6097 100644 --- a/src/v2_0/Exception/Exception.php +++ b/src/Exception/Exception.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v2_0; +namespace PhpCsFixer\Diff; interface Exception { diff --git a/src/v3_0/Exception/InvalidArgumentException.php b/src/Exception/InvalidArgumentException.php similarity index 91% rename from src/v3_0/Exception/InvalidArgumentException.php rename to src/Exception/InvalidArgumentException.php index 50854824..e6946669 100644 --- a/src/v3_0/Exception/InvalidArgumentException.php +++ b/src/Exception/InvalidArgumentException.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0; +namespace PhpCsFixer\Diff; class InvalidArgumentException extends \InvalidArgumentException implements Exception { diff --git a/src/GeckoPackages/DiffOutputBuilder/ConfigurationException.php b/src/GeckoPackages/DiffOutputBuilder/ConfigurationException.php deleted file mode 100644 index 315ac072..00000000 --- a/src/GeckoPackages/DiffOutputBuilder/ConfigurationException.php +++ /dev/null @@ -1,36 +0,0 @@ -' : \gettype($value).'#'.$value) - ), - $code, - $previous - ); - } -} diff --git a/src/GeckoPackages/DiffOutputBuilder/UnifiedDiffOutputBuilder.php b/src/GeckoPackages/DiffOutputBuilder/UnifiedDiffOutputBuilder.php deleted file mode 100644 index 8a703725..00000000 --- a/src/GeckoPackages/DiffOutputBuilder/UnifiedDiffOutputBuilder.php +++ /dev/null @@ -1,295 +0,0 @@ -= 0 - */ - private $commonLineThreshold; - - /** - * @var string - */ - private $header; - - /** - * @var int >= 0 - */ - private $contextLines; - - private static $default = [ - 'contextLines' => 3, // like `diff: -u, -U NUM, --unified[=NUM]`, for patch/git apply compatibility best to keep at least @ 3 - 'collapseRanges' => true, // ranges of length one are rendered with the trailing `,1` - 'fromFile' => null, - 'fromFileDate' => null, - 'toFile' => null, - 'toFileDate' => null, - 'commonLineThreshold' => 6, // number of same lines before ending a new hunk and creating a new one (if needed) - ]; - - public function __construct(array $options = []) - { - $options = \array_merge(self::$default, $options); - - if (!\is_bool($options['collapseRanges'])) { - throw new ConfigurationException('collapseRanges', 'a bool', $options['collapseRanges']); - } - - if (!\is_int($options['contextLines']) || $options['contextLines'] < 0) { - throw new ConfigurationException('contextLines', 'an int >= 0', $options['contextLines']); - } - - if (!\is_int($options['commonLineThreshold']) || $options['commonLineThreshold'] < 1) { - throw new ConfigurationException('commonLineThreshold', 'an int > 0', $options['commonLineThreshold']); - } - - foreach (['fromFile', 'toFile'] as $option) { - if (!\is_string($options[$option])) { - throw new ConfigurationException($option, 'a string', $options[$option]); - } - } - - foreach (['fromFileDate', 'toFileDate'] as $option) { - if (null !== $options[$option] && !\is_string($options[$option])) { - throw new ConfigurationException($option, 'a string or ', $options[$option]); - } - } - - $this->header = \sprintf( - "--- %s%s\n+++ %s%s\n", - $options['fromFile'], - null === $options['fromFileDate'] ? '' : "\t".$options['fromFileDate'], - $options['toFile'], - null === $options['toFileDate'] ? '' : "\t".$options['toFileDate'] - ); - - $this->collapseRanges = $options['collapseRanges']; - $this->commonLineThreshold = $options['commonLineThreshold']; - $this->contextLines = $options['contextLines']; - } - - public function getDiff(array $diff) - { - if (0 === \count($diff)) { - return ''; - } - - $this->changed = false; - - $buffer = \fopen('php://memory', 'r+b'); - \fwrite($buffer, $this->header); - - $this->writeDiffHunks($buffer, $diff); - - $diff = \stream_get_contents($buffer, -1, 0); - - \fclose($buffer); - - if (!$this->changed) { - return ''; - } - - return $diff; - } - - private function writeDiffHunks($output, array $diff) - { - // detect "No newline at end of file" and insert into `$diff` if needed - - $upperLimit = \count($diff); - - // append "\ No newline at end of file" if needed - if (0 === $diff[$upperLimit - 1][1]) { - $lc = \substr($diff[$upperLimit - 1][0], -1); - if ("\n" !== $lc) { - \array_splice($diff, $upperLimit, 0, [["\n\\ No newline at end of file\n", self::$noNewlineAtOEFid]]); - } - } else { - // search back for the last `+` and `-` line, - // check if has trailing linebreak, else add under it warning under it - $toFind = [1 => true, 2 => true]; - for ($i = $upperLimit - 1; $i >= 0; --$i) { - if (isset($toFind[$diff[$i][1]])) { - unset($toFind[$diff[$i][1]]); - $lc = \substr($diff[$i][0], -1); - if ("\n" !== $lc) { - \array_splice($diff, $i + 1, 0, [["\n\\ No newline at end of file\n", self::$noNewlineAtOEFid]]); - } - - if (!\count($toFind)) { - break; - } - } - } - } - - // write hunks to output buffer - - $cutOff = \max($this->commonLineThreshold, $this->contextLines); - $hunkCapture = false; - $sameCount = $toRange = $fromRange = 0; - $toStart = $fromStart = 1; - - foreach ($diff as $i => $entry) { - if (0 === $entry[1]) { // same - if (false === $hunkCapture) { - ++$fromStart; - ++$toStart; - - continue; - } - - ++$sameCount; - ++$toRange; - ++$fromRange; - - if ($sameCount === $cutOff) { - $contextStartOffset = $hunkCapture - $this->contextLines < 0 - ? $hunkCapture - : $this->contextLines - ; - - $contextEndOffset = $i + $this->contextLines >= \count($diff) - ? \count($diff) - $i - : $this->contextLines - ; - - $this->writeHunk( - $diff, - $hunkCapture - $contextStartOffset, - $i - $cutOff + $contextEndOffset + 1, - $fromStart - $contextStartOffset, - $fromRange - $cutOff + $contextStartOffset + $contextEndOffset, - $toStart - $contextStartOffset, - $toRange - $cutOff + $contextStartOffset + $contextEndOffset, - $output - ); - - $fromStart += $fromRange; - $toStart += $toRange; - - $hunkCapture = false; - $sameCount = $toRange = $fromRange = 0; - } - - continue; - } - - $sameCount = 0; - - if ($entry[1] === self::$noNewlineAtOEFid) { - continue; - } - - $this->changed = true; - - if (false === $hunkCapture) { - $hunkCapture = $i; - } - - if (1 === $entry[1]) { // added - ++$toRange; - } - - if (2 === $entry[1]) { // removed - ++$fromRange; - } - } - - if (false !== $hunkCapture) { - $contextStartOffset = $hunkCapture - $this->contextLines < 0 - ? $hunkCapture - : $this->contextLines - ; - - $this->writeHunk( - $diff, - $hunkCapture - $contextStartOffset, - \count($diff), - $fromStart - $contextStartOffset, - $fromRange + $contextStartOffset, - $toStart - $contextStartOffset, - $toRange + $contextStartOffset, - $output - ); - } - } - - private function writeHunk( - array $diff, - $diffStartIndex, - $diffEndIndex, - $fromStart, - $fromRange, - $toStart, - $toRange, - $output - ) { - \fwrite($output, '@@ -'.$fromStart); - - if (!$this->collapseRanges || 1 !== $fromRange) { - \fwrite($output, ','.$fromRange); - } - - \fwrite($output, ' +'.$toStart); - if (!$this->collapseRanges || 1 !== $toRange) { - \fwrite($output, ','.$toRange); - } - - \fwrite($output, " @@\n"); - - for ($i = $diffStartIndex; $i < $diffEndIndex; ++$i) { - if ($diff[$i][1] === 1) { // added - $this->changed = true; - \fwrite($output, '+'.$diff[$i][0]); - } elseif ($diff[$i][1] === 2) { // removed - $this->changed = true; - \fwrite($output, '-'.$diff[$i][0]); - } elseif ($diff[$i][1] === 0) { // same - \fwrite($output, ' '.$diff[$i][0]); - } elseif ($diff[$i][1] === self::$noNewlineAtOEFid) { - $this->changed = true; - \fwrite($output, $diff[$i][0]); - } - } - } -} diff --git a/src/v3_0/Line.php b/src/Line.php similarity index 95% rename from src/v3_0/Line.php rename to src/Line.php index 07be8805..df9d9567 100644 --- a/src/v3_0/Line.php +++ b/src/Line.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0; +namespace PhpCsFixer\Diff; final class Line { diff --git a/src/v3_0/LongestCommonSubsequenceCalculator.php b/src/LongestCommonSubsequenceCalculator.php similarity index 94% rename from src/v3_0/LongestCommonSubsequenceCalculator.php rename to src/LongestCommonSubsequenceCalculator.php index be5ed5a1..d344851c 100644 --- a/src/v3_0/LongestCommonSubsequenceCalculator.php +++ b/src/LongestCommonSubsequenceCalculator.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0; +namespace PhpCsFixer\Diff; interface LongestCommonSubsequenceCalculator { diff --git a/src/v3_0/MemoryEfficientLongestCommonSubsequenceCalculator.php b/src/MemoryEfficientLongestCommonSubsequenceCalculator.php similarity index 98% rename from src/v3_0/MemoryEfficientLongestCommonSubsequenceCalculator.php rename to src/MemoryEfficientLongestCommonSubsequenceCalculator.php index 85a1c4e6..a54e190f 100644 --- a/src/v3_0/MemoryEfficientLongestCommonSubsequenceCalculator.php +++ b/src/MemoryEfficientLongestCommonSubsequenceCalculator.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0; +namespace PhpCsFixer\Diff; final class MemoryEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator { diff --git a/src/v2_0/Output/AbstractChunkOutputBuilder.php b/src/Output/AbstractChunkOutputBuilder.php similarity index 97% rename from src/v2_0/Output/AbstractChunkOutputBuilder.php rename to src/Output/AbstractChunkOutputBuilder.php index d7d78d7c..6fec7045 100644 --- a/src/v2_0/Output/AbstractChunkOutputBuilder.php +++ b/src/Output/AbstractChunkOutputBuilder.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v2_0\Output; +namespace PhpCsFixer\Diff\Output; abstract class AbstractChunkOutputBuilder implements DiffOutputBuilderInterface { diff --git a/src/v3_0/Output/DiffOnlyOutputBuilder.php b/src/Output/DiffOnlyOutputBuilder.php similarity index 96% rename from src/v3_0/Output/DiffOnlyOutputBuilder.php rename to src/Output/DiffOnlyOutputBuilder.php index 0f3b81f5..b010040a 100644 --- a/src/v3_0/Output/DiffOnlyOutputBuilder.php +++ b/src/Output/DiffOnlyOutputBuilder.php @@ -8,9 +8,9 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0\Output; +namespace PhpCsFixer\Diff\Output; -use PhpCsFixer\Diff\v3_0\Differ; +use PhpCsFixer\Diff\Differ; /** * Builds a diff string representation in a loose unified diff format diff --git a/src/v3_0/Output/DiffOutputBuilderInterface.php b/src/Output/DiffOutputBuilderInterface.php similarity index 91% rename from src/v3_0/Output/DiffOutputBuilderInterface.php rename to src/Output/DiffOutputBuilderInterface.php index ae690e6a..d348ed1f 100644 --- a/src/v3_0/Output/DiffOutputBuilderInterface.php +++ b/src/Output/DiffOutputBuilderInterface.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0\Output; +namespace PhpCsFixer\Diff\Output; /** * Defines how an output builder should take a generated diff --git a/src/v3_0/Output/StrictUnifiedDiffOutputBuilder.php b/src/Output/StrictUnifiedDiffOutputBuilder.php similarity index 98% rename from src/v3_0/Output/StrictUnifiedDiffOutputBuilder.php rename to src/Output/StrictUnifiedDiffOutputBuilder.php index 49faa8ad..4b080554 100644 --- a/src/v3_0/Output/StrictUnifiedDiffOutputBuilder.php +++ b/src/Output/StrictUnifiedDiffOutputBuilder.php @@ -8,10 +8,10 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0\Output; +namespace PhpCsFixer\Diff\Output; -use PhpCsFixer\Diff\v3_0\ConfigurationException; -use PhpCsFixer\Diff\v3_0\Differ; +use PhpCsFixer\Diff\ConfigurationException; +use PhpCsFixer\Diff\Differ; /** * Strict Unified diff output builder. diff --git a/src/v3_0/Output/UnifiedDiffOutputBuilder.php b/src/Output/UnifiedDiffOutputBuilder.php similarity index 99% rename from src/v3_0/Output/UnifiedDiffOutputBuilder.php rename to src/Output/UnifiedDiffOutputBuilder.php index 5e7276ab..69a12708 100644 --- a/src/v3_0/Output/UnifiedDiffOutputBuilder.php +++ b/src/Output/UnifiedDiffOutputBuilder.php @@ -8,9 +8,9 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0\Output; +namespace PhpCsFixer\Diff\Output; -use PhpCsFixer\Diff\v3_0\Differ; +use PhpCsFixer\Diff\Differ; /** * Builds a diff string representation in unified diff format in chunks. diff --git a/src/v3_0/Parser.php b/src/Parser.php similarity index 98% rename from src/v3_0/Parser.php rename to src/Parser.php index aa7175a3..87afb93f 100644 --- a/src/v3_0/Parser.php +++ b/src/Parser.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0; +namespace PhpCsFixer\Diff; /** * Unified diff parser. diff --git a/src/v3_0/TimeEfficientLongestCommonSubsequenceCalculator.php b/src/TimeEfficientLongestCommonSubsequenceCalculator.php similarity index 98% rename from src/v3_0/TimeEfficientLongestCommonSubsequenceCalculator.php rename to src/TimeEfficientLongestCommonSubsequenceCalculator.php index 644968ac..5e4d51d5 100644 --- a/src/v3_0/TimeEfficientLongestCommonSubsequenceCalculator.php +++ b/src/TimeEfficientLongestCommonSubsequenceCalculator.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0; +namespace PhpCsFixer\Diff; final class TimeEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator { diff --git a/src/v1_4/Chunk.php b/src/v1_4/Chunk.php deleted file mode 100644 index b247e030..00000000 --- a/src/v1_4/Chunk.php +++ /dev/null @@ -1,103 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v1_4; - -class Chunk -{ - /** - * @var int - */ - private $start; - - /** - * @var int - */ - private $startRange; - - /** - * @var int - */ - private $end; - - /** - * @var int - */ - private $endRange; - - /** - * @var array - */ - private $lines; - - /** - * @param int $start - * @param int $startRange - * @param int $end - * @param int $endRange - * @param array $lines - */ - public function __construct($start = 0, $startRange = 1, $end = 0, $endRange = 1, array $lines = array()) - { - $this->start = (int) $start; - $this->startRange = (int) $startRange; - $this->end = (int) $end; - $this->endRange = (int) $endRange; - $this->lines = $lines; - } - - /** - * @return int - */ - public function getStart() - { - return $this->start; - } - - /** - * @return int - */ - public function getStartRange() - { - return $this->startRange; - } - - /** - * @return int - */ - public function getEnd() - { - return $this->end; - } - - /** - * @return int - */ - public function getEndRange() - { - return $this->endRange; - } - - /** - * @return array - */ - public function getLines() - { - return $this->lines; - } - - /** - * @param array $lines - */ - public function setLines(array $lines) - { - $this->lines = $lines; - } -} diff --git a/src/v1_4/Diff.php b/src/v1_4/Diff.php deleted file mode 100644 index 695a519b..00000000 --- a/src/v1_4/Diff.php +++ /dev/null @@ -1,73 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v1_4; - -class Diff -{ - /** - * @var string - */ - private $from; - - /** - * @var string - */ - private $to; - - /** - * @var Chunk[] - */ - private $chunks; - - /** - * @param string $from - * @param string $to - * @param Chunk[] $chunks - */ - public function __construct($from, $to, array $chunks = array()) - { - $this->from = $from; - $this->to = $to; - $this->chunks = $chunks; - } - - /** - * @return string - */ - public function getFrom() - { - return $this->from; - } - - /** - * @return string - */ - public function getTo() - { - return $this->to; - } - - /** - * @return Chunk[] - */ - public function getChunks() - { - return $this->chunks; - } - - /** - * @param Chunk[] $chunks - */ - public function setChunks(array $chunks) - { - $this->chunks = $chunks; - } -} diff --git a/src/v1_4/Differ.php b/src/v1_4/Differ.php deleted file mode 100644 index b2015abf..00000000 --- a/src/v1_4/Differ.php +++ /dev/null @@ -1,399 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v1_4; - -use PhpCsFixer\Diff\v1_4\LCS\LongestCommonSubsequence; -use PhpCsFixer\Diff\v1_4\LCS\TimeEfficientImplementation; -use PhpCsFixer\Diff\v1_4\LCS\MemoryEfficientImplementation; - -/** - * Diff implementation. - */ -class Differ -{ - /** - * @var string - */ - private $header; - - /** - * @var bool - */ - private $showNonDiffLines; - - /** - * @param string $header - * @param bool $showNonDiffLines - */ - public function __construct($header = "--- Original\n+++ New\n", $showNonDiffLines = true) - { - $this->header = $header; - $this->showNonDiffLines = $showNonDiffLines; - } - - /** - * Returns the diff between two arrays or strings as string. - * - * @param array|string $from - * @param array|string $to - * @param LongestCommonSubsequence $lcs - * - * @return string - */ - public function diff($from, $to, LongestCommonSubsequence $lcs = null) - { - $from = $this->validateDiffInput($from); - $to = $this->validateDiffInput($to); - $diff = $this->diffToArray($from, $to, $lcs); - $old = $this->checkIfDiffInOld($diff); - $start = isset($old[0]) ? $old[0] : 0; - $end = \count($diff); - - if ($tmp = \array_search($end, $old)) { - $end = $tmp; - } - - return $this->getBuffer($diff, $old, $start, $end); - } - - /** - * Casts variable to string if it is not a string or array. - * - * @param mixed $input - * - * @return string - */ - private function validateDiffInput($input) - { - if (!\is_array($input) && !\is_string($input)) { - return (string) $input; - } - - return $input; - } - - /** - * Takes input of the diff array and returns the old array. - * Iterates through diff line by line, - * - * @param array $diff - * - * @return array - */ - private function checkIfDiffInOld(array $diff) - { - $inOld = false; - $i = 0; - $old = array(); - - foreach ($diff as $line) { - if ($line[1] === 0 /* OLD */) { - if ($inOld === false) { - $inOld = $i; - } - } elseif ($inOld !== false) { - if (($i - $inOld) > 5) { - $old[$inOld] = $i - 1; - } - - $inOld = false; - } - - ++$i; - } - - return $old; - } - - /** - * Generates buffer in string format, returning the patch. - * - * @param array $diff - * @param array $old - * @param int $start - * @param int $end - * - * @return string - */ - private function getBuffer(array $diff, array $old, $start, $end) - { - $buffer = $this->header; - - if (!isset($old[$start])) { - $buffer = $this->getDiffBufferElementNew($diff, $buffer, $start); - ++$start; - } - - for ($i = $start; $i < $end; $i++) { - if (isset($old[$i])) { - $i = $old[$i]; - $buffer = $this->getDiffBufferElementNew($diff, $buffer, $i); - } else { - $buffer = $this->getDiffBufferElement($diff, $buffer, $i); - } - } - - return $buffer; - } - - /** - * Gets individual buffer element. - * - * @param array $diff - * @param string $buffer - * @param int $diffIndex - * - * @return string - */ - private function getDiffBufferElement(array $diff, $buffer, $diffIndex) - { - if ($diff[$diffIndex][1] === 1 /* ADDED */) { - $buffer .= '+' . $diff[$diffIndex][0] . "\n"; - } elseif ($diff[$diffIndex][1] === 2 /* REMOVED */) { - $buffer .= '-' . $diff[$diffIndex][0] . "\n"; - } elseif ($this->showNonDiffLines === true) { - $buffer .= ' ' . $diff[$diffIndex][0] . "\n"; - } - - return $buffer; - } - - /** - * Gets individual buffer element with opening. - * - * @param array $diff - * @param string $buffer - * @param int $diffIndex - * - * @return string - */ - private function getDiffBufferElementNew(array $diff, $buffer, $diffIndex) - { - if ($this->showNonDiffLines === true) { - $buffer .= "@@ @@\n"; - } - - return $this->getDiffBufferElement($diff, $buffer, $diffIndex); - } - - /** - * Returns the diff between two arrays or strings as array. - * - * Each array element contains two elements: - * - [0] => mixed $token - * - [1] => 2|1|0 - * - * - 2: REMOVED: $token was removed from $from - * - 1: ADDED: $token was added to $from - * - 0: OLD: $token is not changed in $to - * - * @param array|string $from - * @param array|string $to - * @param LongestCommonSubsequence $lcs - * - * @return array - */ - public function diffToArray($from, $to, LongestCommonSubsequence $lcs = null) - { - if (\is_string($from)) { - $fromMatches = $this->getNewLineMatches($from); - $from = $this->splitStringByLines($from); - } elseif (\is_array($from)) { - $fromMatches = array(); - } else { - throw new \InvalidArgumentException('"from" must be an array or string.'); - } - - if (\is_string($to)) { - $toMatches = $this->getNewLineMatches($to); - $to = $this->splitStringByLines($to); - } elseif (\is_array($to)) { - $toMatches = array(); - } else { - throw new \InvalidArgumentException('"to" must be an array or string.'); - } - - list($from, $to, $start, $end) = self::getArrayDiffParted($from, $to); - - if ($lcs === null) { - $lcs = $this->selectLcsImplementation($from, $to); - } - - $common = $lcs->calculate(\array_values($from), \array_values($to)); - $diff = array(); - - if ($this->detectUnmatchedLineEndings($fromMatches, $toMatches)) { - $diff[] = array( - '#Warnings contain different line endings!', - 0 - ); - } - - foreach ($start as $token) { - $diff[] = array($token, 0 /* OLD */); - } - - \reset($from); - \reset($to); - - foreach ($common as $token) { - while (($fromToken = \reset($from)) !== $token) { - $diff[] = array(\array_shift($from), 2 /* REMOVED */); - } - - while (($toToken = \reset($to)) !== $token) { - $diff[] = array(\array_shift($to), 1 /* ADDED */); - } - - $diff[] = array($token, 0 /* OLD */); - - \array_shift($from); - \array_shift($to); - } - - while (($token = \array_shift($from)) !== null) { - $diff[] = array($token, 2 /* REMOVED */); - } - - while (($token = \array_shift($to)) !== null) { - $diff[] = array($token, 1 /* ADDED */); - } - - foreach ($end as $token) { - $diff[] = array($token, 0 /* OLD */); - } - - return $diff; - } - - /** - * Get new strings denoting new lines from a given string. - * - * @param string $string - * - * @return array - */ - private function getNewLineMatches($string) - { - \preg_match_all('(\r\n|\r|\n)', $string, $stringMatches); - - return $stringMatches; - } - - /** - * Checks if input is string, if so it will split it line-by-line. - * - * @param string $input - * - * @return array - */ - private function splitStringByLines($input) - { - return \preg_split('(\r\n|\r|\n)', $input); - } - - /** - * @param array $from - * @param array $to - * - * @return LongestCommonSubsequence - */ - private function selectLcsImplementation(array $from, array $to) - { - // We do not want to use the time-efficient implementation if its memory - // footprint will probably exceed this value. Note that the footprint - // calculation is only an estimation for the matrix and the LCS method - // will typically allocate a bit more memory than this. - $memoryLimit = 100 * 1024 * 1024; - - if ($this->calculateEstimatedFootprint($from, $to) > $memoryLimit) { - return new MemoryEfficientImplementation; - } - - return new TimeEfficientImplementation; - } - - /** - * Calculates the estimated memory footprint for the DP-based method. - * - * @param array $from - * @param array $to - * - * @return int|float - */ - private function calculateEstimatedFootprint(array $from, array $to) - { - $itemSize = PHP_INT_SIZE === 4 ? 76 : 144; - - return $itemSize * \pow(\min(\count($from), \count($to)), 2); - } - - /** - * Returns true if line ends don't match on fromMatches and toMatches. - * - * @param array $fromMatches - * @param array $toMatches - * - * @return bool - */ - private function detectUnmatchedLineEndings(array $fromMatches, array $toMatches) - { - return isset($fromMatches[0], $toMatches[0]) && - \count($fromMatches[0]) === \count($toMatches[0]) && - $fromMatches[0] !== $toMatches[0]; - } - - /** - * @param array $from - * @param array $to - * - * @return array - */ - private static function getArrayDiffParted(array &$from, array &$to) - { - $start = array(); - $end = array(); - - \reset($to); - - foreach ($from as $k => $v) { - $toK = \key($to); - - if ($toK === $k && $v === $to[$k]) { - $start[$k] = $v; - - unset($from[$k], $to[$k]); - } else { - break; - } - } - - \end($from); - \end($to); - - do { - $fromK = \key($from); - $toK = \key($to); - - if (null === $fromK || null === $toK || \current($from) !== \current($to)) { - break; - } - - \prev($from); - \prev($to); - - $end = array($fromK => $from[$fromK]) + $end; - unset($from[$fromK], $to[$toK]); - } while (true); - - return array($from, $to, $start, $end); - } -} diff --git a/src/v1_4/LCS/LongestCommonSubsequence.php b/src/v1_4/LCS/LongestCommonSubsequence.php deleted file mode 100644 index dad5a0cc..00000000 --- a/src/v1_4/LCS/LongestCommonSubsequence.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v1_4\LCS; - -/** - * Interface for implementations of longest common subsequence calculation. - */ -interface LongestCommonSubsequence -{ - /** - * Calculates the longest common subsequence of two arrays. - * - * @param array $from - * @param array $to - * - * @return array - */ - public function calculate(array $from, array $to); -} diff --git a/src/v1_4/LCS/MemoryEfficientLongestCommonSubsequenceImplementation.php b/src/v1_4/LCS/MemoryEfficientLongestCommonSubsequenceImplementation.php deleted file mode 100644 index 66852525..00000000 --- a/src/v1_4/LCS/MemoryEfficientLongestCommonSubsequenceImplementation.php +++ /dev/null @@ -1,95 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v1_4\LCS; - -/** - * Memory-efficient implementation of longest common subsequence calculation. - */ -class MemoryEfficientImplementation implements LongestCommonSubsequence -{ - /** - * Calculates the longest common subsequence of two arrays. - * - * @param array $from - * @param array $to - * - * @return array - */ - public function calculate(array $from, array $to) - { - $cFrom = \count($from); - $cTo = \count($to); - - if ($cFrom === 0) { - return array(); - } - - if ($cFrom === 1) { - if (\in_array($from[0], $to, true)) { - return array($from[0]); - } - - return array(); - } - - $i = (int) ($cFrom / 2); - $fromStart = \array_slice($from, 0, $i); - $fromEnd = \array_slice($from, $i); - $llB = $this->length($fromStart, $to); - $llE = $this->length(\array_reverse($fromEnd), \array_reverse($to)); - $jMax = 0; - $max = 0; - - for ($j = 0; $j <= $cTo; $j++) { - $m = $llB[$j] + $llE[$cTo - $j]; - - if ($m >= $max) { - $max = $m; - $jMax = $j; - } - } - - $toStart = \array_slice($to, 0, $jMax); - $toEnd = \array_slice($to, $jMax); - - return \array_merge( - $this->calculate($fromStart, $toStart), - $this->calculate($fromEnd, $toEnd) - ); - } - - /** - * @param array $from - * @param array $to - * - * @return array - */ - private function length(array $from, array $to) - { - $current = \array_fill(0, \count($to) + 1, 0); - $cFrom = \count($from); - $cTo = \count($to); - - for ($i = 0; $i < $cFrom; $i++) { - $prev = $current; - - for ($j = 0; $j < $cTo; $j++) { - if ($from[$i] === $to[$j]) { - $current[$j + 1] = $prev[$j] + 1; - } else { - $current[$j + 1] = \max($current[$j], $prev[$j + 1]); - } - } - } - - return $current; - } -} diff --git a/src/v1_4/LCS/TimeEfficientLongestCommonSubsequenceImplementation.php b/src/v1_4/LCS/TimeEfficientLongestCommonSubsequenceImplementation.php deleted file mode 100644 index 4acd68ef..00000000 --- a/src/v1_4/LCS/TimeEfficientLongestCommonSubsequenceImplementation.php +++ /dev/null @@ -1,74 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v1_4\LCS; - -/** - * Time-efficient implementation of longest common subsequence calculation. - */ -class TimeEfficientImplementation implements LongestCommonSubsequence -{ - /** - * Calculates the longest common subsequence of two arrays. - * - * @param array $from - * @param array $to - * - * @return array - */ - public function calculate(array $from, array $to) - { - $common = array(); - $fromLength = \count($from); - $toLength = \count($to); - $width = $fromLength + 1; - $matrix = new \SplFixedArray($width * ($toLength + 1)); - - for ($i = 0; $i <= $fromLength; ++$i) { - $matrix[$i] = 0; - } - - for ($j = 0; $j <= $toLength; ++$j) { - $matrix[$j * $width] = 0; - } - - for ($i = 1; $i <= $fromLength; ++$i) { - for ($j = 1; $j <= $toLength; ++$j) { - $o = ($j * $width) + $i; - $matrix[$o] = \max( - $matrix[$o - 1], - $matrix[$o - $width], - $from[$i - 1] === $to[$j - 1] ? $matrix[$o - $width - 1] + 1 : 0 - ); - } - } - - $i = $fromLength; - $j = $toLength; - - while ($i > 0 && $j > 0) { - if ($from[$i - 1] === $to[$j - 1]) { - $common[] = $from[$i - 1]; - --$i; - --$j; - } else { - $o = ($j * $width) + $i; - - if ($matrix[$o - $width] > $matrix[$o - 1]) { - --$j; - } else { - --$i; - } - } - } - - return \array_reverse($common); - } -} diff --git a/src/v1_4/Line.php b/src/v1_4/Line.php deleted file mode 100644 index 1187bcde..00000000 --- a/src/v1_4/Line.php +++ /dev/null @@ -1,54 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v1_4; - -class Line -{ - const ADDED = 1; - const REMOVED = 2; - const UNCHANGED = 3; - - /** - * @var int - */ - private $type; - - /** - * @var string - */ - private $content; - - /** - * @param int $type - * @param string $content - */ - public function __construct($type = self::UNCHANGED, $content = '') - { - $this->type = $type; - $this->content = $content; - } - - /** - * @return string - */ - public function getContent() - { - return $this->content; - } - - /** - * @return int - */ - public function getType() - { - return $this->type; - } -} diff --git a/src/v1_4/Parser.php b/src/v1_4/Parser.php deleted file mode 100644 index 6f8e75b5..00000000 --- a/src/v1_4/Parser.php +++ /dev/null @@ -1,110 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v1_4; - -/** - * Unified diff parser. - */ -class Parser -{ - /** - * @param string $string - * - * @return Diff[] - */ - public function parse($string) - { - $lines = \preg_split('(\r\n|\r|\n)', $string); - - if (!empty($lines) && $lines[\count($lines) - 1] == '') { - \array_pop($lines); - } - - $lineCount = \count($lines); - $diffs = array(); - $diff = null; - $collected = array(); - - for ($i = 0; $i < $lineCount; ++$i) { - if (\preg_match('(^---\\s+(?P\\S+))', $lines[$i], $fromMatch) && - \preg_match('(^\\+\\+\\+\\s+(?P\\S+))', $lines[$i + 1], $toMatch)) { - if ($diff !== null) { - $this->parseFileDiff($diff, $collected); - - $diffs[] = $diff; - $collected = array(); - } - - $diff = new Diff($fromMatch['file'], $toMatch['file']); - - ++$i; - } else { - if (\preg_match('/^(?:diff --git |index [\da-f\.]+|[+-]{3} [ab])/', $lines[$i])) { - continue; - } - - $collected[] = $lines[$i]; - } - } - - if ($diff !== null && \count($collected)) { - $this->parseFileDiff($diff, $collected); - - $diffs[] = $diff; - } - - return $diffs; - } - - /** - * @param Diff $diff - * @param array $lines - */ - private function parseFileDiff(Diff $diff, array $lines) - { - $chunks = array(); - $chunk = null; - - foreach ($lines as $line) { - if (\preg_match('/^@@\s+-(?P\d+)(?:,\s*(?P\d+))?\s+\+(?P\d+)(?:,\s*(?P\d+))?\s+@@/', $line, $match)) { - $chunk = new Chunk( - $match['start'], - isset($match['startrange']) ? \max(1, $match['startrange']) : 1, - $match['end'], - isset($match['endrange']) ? \max(1, $match['endrange']) : 1 - ); - - $chunks[] = $chunk; - $diffLines = array(); - - continue; - } - - if (\preg_match('/^(?P[+ -])?(?P.*)/', $line, $match)) { - $type = Line::UNCHANGED; - - if ($match['type'] === '+') { - $type = Line::ADDED; - } elseif ($match['type'] === '-') { - $type = Line::REMOVED; - } - - $diffLines[] = new Line($type, $match['line']); - - if (null !== $chunk) { - $chunk->setLines($diffLines); - } - } - } - - $diff->setChunks($chunks); - } -} diff --git a/src/v2_0/Chunk.php b/src/v2_0/Chunk.php deleted file mode 100644 index b80ddfcf..00000000 --- a/src/v2_0/Chunk.php +++ /dev/null @@ -1,78 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v2_0; - -final class Chunk -{ - /** - * @var int - */ - private $start; - - /** - * @var int - */ - private $startRange; - - /** - * @var int - */ - private $end; - - /** - * @var int - */ - private $endRange; - - /** - * @var array - */ - private $lines; - - public function __construct($start = 0, $startRange = 1, $end = 0, $endRange = 1, array $lines = []) - { - $this->start = $start; - $this->startRange = $startRange; - $this->end = $end; - $this->endRange = $endRange; - $this->lines = $lines; - } - - public function getStart() - { - return $this->start; - } - - public function getStartRange() - { - return $this->startRange; - } - - public function getEnd() - { - return $this->end; - } - - public function getEndRange() - { - return $this->endRange; - } - - public function getLines() - { - return $this->lines; - } - - public function setLines(array $lines) - { - $this->lines = $lines; - } -} diff --git a/src/v2_0/Diff.php b/src/v2_0/Diff.php deleted file mode 100644 index e89b8e9d..00000000 --- a/src/v2_0/Diff.php +++ /dev/null @@ -1,67 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v2_0; - -final class Diff -{ - /** - * @var string - */ - private $from; - - /** - * @var string - */ - private $to; - - /** - * @var Chunk[] - */ - private $chunks; - - /** - * @param string $from - * @param string $to - * @param Chunk[] $chunks - */ - public function __construct($from, $to, array $chunks = []) - { - $this->from = $from; - $this->to = $to; - $this->chunks = $chunks; - } - - public function getFrom() - { - return $this->from; - } - - public function getTo() - { - return $this->to; - } - - /** - * @return Chunk[] - */ - public function getChunks() - { - return $this->chunks; - } - - /** - * @param Chunk[] $chunks - */ - public function setChunks(array $chunks) - { - $this->chunks = $chunks; - } -} diff --git a/src/v2_0/Differ.php b/src/v2_0/Differ.php deleted file mode 100644 index a8f7dba7..00000000 --- a/src/v2_0/Differ.php +++ /dev/null @@ -1,321 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v2_0; - -use PhpCsFixer\Diff\v2_0\Output\DiffOutputBuilderInterface; -use PhpCsFixer\Diff\v2_0\Output\UnifiedDiffOutputBuilder; - -/** - * Diff implementation. - */ -final class Differ -{ - /** - * @var DiffOutputBuilderInterface - */ - private $outputBuilder; - - /** - * @param DiffOutputBuilderInterface $outputBuilder - * - * @throws InvalidArgumentException - */ - public function __construct($outputBuilder = null) - { - if ($outputBuilder instanceof DiffOutputBuilderInterface) { - $this->outputBuilder = $outputBuilder; - } elseif (null === $outputBuilder) { - $this->outputBuilder = new UnifiedDiffOutputBuilder; - } elseif (\is_string($outputBuilder)) { - // PHPUnit 6.1.4, 6.2.0, 6.2.1, 6.2.2, and 6.2.3 support - // @see https://github.com/sebastianbergmann/phpunit/issues/2734#issuecomment-314514056 - // @deprecated - $this->outputBuilder = new UnifiedDiffOutputBuilder($outputBuilder); - } else { - throw new InvalidArgumentException( - \sprintf( - 'Expected builder to be an instance of DiffOutputBuilderInterface, or a string, got %s.', - \is_object($outputBuilder) ? 'instance of "' . \get_class($outputBuilder) . '"' : \gettype($outputBuilder) . ' "' . $outputBuilder . '"' - ) - ); - } - } - - /** - * Returns the diff between two arrays or strings as string. - * - * @param array|string $from - * @param array|string $to - * @param LongestCommonSubsequenceCalculator|null $lcs - * - * @return string - */ - public function diff($from, $to, LongestCommonSubsequenceCalculator $lcs = null) - { - $from = $this->validateDiffInput($from); - $to = $this->validateDiffInput($to); - $diff = $this->diffToArray($from, $to, $lcs); - - return $this->outputBuilder->getDiff($diff); - } - - /** - * Casts variable to string if it is not a string or array. - * - * @param mixed $input - * - * @return string - */ - private function validateDiffInput($input) - { - if (!\is_array($input) && !\is_string($input)) { - return (string) $input; - } - - return $input; - } - - /** - * Returns the diff between two arrays or strings as array. - * - * Each array element contains two elements: - * - [0] => mixed $token - * - [1] => 2|1|0 - * - * - 2: REMOVED: $token was removed from $from - * - 1: ADDED: $token was added to $from - * - 0: OLD: $token is not changed in $to - * - * @param array|string $from - * @param array|string $to - * @param LongestCommonSubsequenceCalculator $lcs - * - * @return array - */ - public function diffToArray($from, $to, LongestCommonSubsequenceCalculator $lcs = null) - { - if (\is_string($from)) { - $from = $this->splitStringByLines($from); - } elseif (!\is_array($from)) { - throw new \InvalidArgumentException('"from" must be an array or string.'); - } - - if (\is_string($to)) { - $to = $this->splitStringByLines($to); - } elseif (!\is_array($to)) { - throw new \InvalidArgumentException('"to" must be an array or string.'); - } - - list($from, $to, $start, $end) = self::getArrayDiffParted($from, $to); - - if ($lcs === null) { - $lcs = $this->selectLcsImplementation($from, $to); - } - - $common = $lcs->calculate(\array_values($from), \array_values($to)); - $diff = []; - - foreach ($start as $token) { - $diff[] = [$token, 0 /* OLD */]; - } - - \reset($from); - \reset($to); - - foreach ($common as $token) { - while (($fromToken = \reset($from)) !== $token) { - $diff[] = [\array_shift($from), 2 /* REMOVED */]; - } - - while (($toToken = \reset($to)) !== $token) { - $diff[] = [\array_shift($to), 1 /* ADDED */]; - } - - $diff[] = [$token, 0 /* OLD */]; - - \array_shift($from); - \array_shift($to); - } - - while (($token = \array_shift($from)) !== null) { - $diff[] = [$token, 2 /* REMOVED */]; - } - - while (($token = \array_shift($to)) !== null) { - $diff[] = [$token, 1 /* ADDED */]; - } - - foreach ($end as $token) { - $diff[] = [$token, 0 /* OLD */]; - } - - if ($this->detectUnmatchedLineEndings($diff)) { - \array_unshift($diff, ["#Warnings contain different line endings!\n", 3]); - } - - return $diff; - } - - /** - * Checks if input is string, if so it will split it line-by-line. - * - * @param string $input - * - * @return array - */ - private function splitStringByLines($input) - { - return \preg_split('/(.*\R)/', $input, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); - } - - /** - * @param array $from - * @param array $to - * - * @return LongestCommonSubsequenceCalculator - */ - private function selectLcsImplementation(array $from, array $to) - { - // We do not want to use the time-efficient implementation if its memory - // footprint will probably exceed this value. Note that the footprint - // calculation is only an estimation for the matrix and the LCS method - // will typically allocate a bit more memory than this. - $memoryLimit = 100 * 1024 * 1024; - - if ($this->calculateEstimatedFootprint($from, $to) > $memoryLimit) { - return new MemoryEfficientLongestCommonSubsequenceCalculator; - } - - return new TimeEfficientLongestCommonSubsequenceCalculator; - } - - /** - * Calculates the estimated memory footprint for the DP-based method. - * - * @param array $from - * @param array $to - * - * @return int|float - */ - private function calculateEstimatedFootprint(array $from, array $to) - { - $itemSize = PHP_INT_SIZE === 4 ? 76 : 144; - - return $itemSize * \min(\count($from), \count($to)) ** 2; - } - - /** - * Returns true if line ends don't match in a diff. - * - * @param array $diff - * - * @return bool - */ - private function detectUnmatchedLineEndings(array $diff) - { - $newLineBreaks = ['' => true]; - $oldLineBreaks = ['' => true]; - - foreach ($diff as $entry) { - if (0 === $entry[1]) { /* OLD */ - $ln = $this->getLinebreak($entry[0]); - $oldLineBreaks[$ln] = true; - $newLineBreaks[$ln] = true; - } elseif (1 === $entry[1]) { /* ADDED */ - $newLineBreaks[$this->getLinebreak($entry[0])] = true; - } elseif (2 === $entry[1]) { /* REMOVED */ - $oldLineBreaks[$this->getLinebreak($entry[0])] = true; - } - } - - // if either input or output is a single line without breaks than no warning should be raised - if (['' => true] === $newLineBreaks || ['' => true] === $oldLineBreaks) { - return false; - } - - // two way compare - foreach ($newLineBreaks as $break => $set) { - if (!isset($oldLineBreaks[$break])) { - return true; - } - } - - foreach ($oldLineBreaks as $break => $set) { - if (!isset($newLineBreaks[$break])) { - return true; - } - } - - return false; - } - - private function getLinebreak($line) - { - if (!\is_string($line)) { - return ''; - } - - $lc = \substr($line, -1); - if ("\r" === $lc) { - return "\r"; - } - - if ("\n" !== $lc) { - return ''; - } - - if ("\r\n" === \substr($line, -2)) { - return "\r\n"; - } - - return "\n"; - } - - private static function getArrayDiffParted(array &$from, array &$to) - { - $start = []; - $end = []; - - \reset($to); - - foreach ($from as $k => $v) { - $toK = \key($to); - - if ($toK === $k && $v === $to[$k]) { - $start[$k] = $v; - - unset($from[$k], $to[$k]); - } else { - break; - } - } - - \end($from); - \end($to); - - do { - $fromK = \key($from); - $toK = \key($to); - - if (null === $fromK || null === $toK || \current($from) !== \current($to)) { - break; - } - - \prev($from); - \prev($to); - - $end = [$fromK => $from[$fromK]] + $end; - unset($from[$fromK], $to[$toK]); - } while (true); - - return [$from, $to, $start, $end]; - } -} diff --git a/src/v2_0/Exception/InvalidArgumentException.php b/src/v2_0/Exception/InvalidArgumentException.php deleted file mode 100644 index e1a0c0b5..00000000 --- a/src/v2_0/Exception/InvalidArgumentException.php +++ /dev/null @@ -1,15 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v2_0; - -class InvalidArgumentException extends \InvalidArgumentException implements Exception -{ -} diff --git a/src/v2_0/Line.php b/src/v2_0/Line.php deleted file mode 100644 index 75d5ec85..00000000 --- a/src/v2_0/Line.php +++ /dev/null @@ -1,44 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v2_0; - -final class Line -{ - const ADDED = 1; - const REMOVED = 2; - const UNCHANGED = 3; - - /** - * @var int - */ - private $type; - - /** - * @var string - */ - private $content; - - public function __construct($type = self::UNCHANGED, $content = '') - { - $this->type = $type; - $this->content = $content; - } - - public function getContent() - { - return $this->content; - } - - public function getType() - { - return $this->type; - } -} diff --git a/src/v2_0/LongestCommonSubsequenceCalculator.php b/src/v2_0/LongestCommonSubsequenceCalculator.php deleted file mode 100644 index 25eda02a..00000000 --- a/src/v2_0/LongestCommonSubsequenceCalculator.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v2_0; - -interface LongestCommonSubsequenceCalculator -{ - /** - * Calculates the longest common subsequence of two arrays. - * - * @param array $from - * @param array $to - * - * @return array - */ - public function calculate(array $from, array $to); -} diff --git a/src/v2_0/MemoryEfficientLongestCommonSubsequenceCalculator.php b/src/v2_0/MemoryEfficientLongestCommonSubsequenceCalculator.php deleted file mode 100644 index e6ce284f..00000000 --- a/src/v2_0/MemoryEfficientLongestCommonSubsequenceCalculator.php +++ /dev/null @@ -1,81 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v2_0; - -final class MemoryEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator -{ - /** - * {@inheritdoc} - */ - public function calculate(array $from, array $to) - { - $cFrom = \count($from); - $cTo = \count($to); - - if ($cFrom === 0) { - return []; - } - - if ($cFrom === 1) { - if (\in_array($from[0], $to, true)) { - return [$from[0]]; - } - - return []; - } - - $i = (int) ($cFrom / 2); - $fromStart = \array_slice($from, 0, $i); - $fromEnd = \array_slice($from, $i); - $llB = $this->length($fromStart, $to); - $llE = $this->length(\array_reverse($fromEnd), \array_reverse($to)); - $jMax = 0; - $max = 0; - - for ($j = 0; $j <= $cTo; $j++) { - $m = $llB[$j] + $llE[$cTo - $j]; - - if ($m >= $max) { - $max = $m; - $jMax = $j; - } - } - - $toStart = \array_slice($to, 0, $jMax); - $toEnd = \array_slice($to, $jMax); - - return \array_merge( - $this->calculate($fromStart, $toStart), - $this->calculate($fromEnd, $toEnd) - ); - } - - private function length(array $from, array $to) - { - $current = \array_fill(0, \count($to) + 1, 0); - $cFrom = \count($from); - $cTo = \count($to); - - for ($i = 0; $i < $cFrom; $i++) { - $prev = $current; - - for ($j = 0; $j < $cTo; $j++) { - if ($from[$i] === $to[$j]) { - $current[$j + 1] = $prev[$j] + 1; - } else { - $current[$j + 1] = \max($current[$j], $prev[$j + 1]); - } - } - } - - return $current; - } -} diff --git a/src/v2_0/Output/DiffOnlyOutputBuilder.php b/src/v2_0/Output/DiffOnlyOutputBuilder.php deleted file mode 100644 index 51cb2dec..00000000 --- a/src/v2_0/Output/DiffOnlyOutputBuilder.php +++ /dev/null @@ -1,63 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PhpCsFixer\Diff\v2_0\Output; - -/** - * Builds a diff string representation in a loose unified diff format - * listing only changes lines. Does not include line numbers. - */ -final class DiffOnlyOutputBuilder implements DiffOutputBuilderInterface -{ - /** - * @var string - */ - private $header; - - public function __construct($header = "--- Original\n+++ New\n") - { - $this->header = $header; - } - - public function getDiff(array $diff) - { - $buffer = \fopen('php://memory', 'r+b'); - - if ('' !== $this->header) { - \fwrite($buffer, $this->header); - if ("\n" !== \substr($this->header, -1, 1)) { - \fwrite($buffer, "\n"); - } - } - - foreach ($diff as $diffEntry) { - if ($diffEntry[1] === 1 /* ADDED */) { - \fwrite($buffer, '+' . $diffEntry[0]); - } elseif ($diffEntry[1] === 2 /* REMOVED */) { - \fwrite($buffer, '-' . $diffEntry[0]); - } elseif ($diffEntry[1] === 3 /* WARNING */) { - \fwrite($buffer, ' ' . $diffEntry[0]); - - continue; // Warnings should not be tested for line break, it will always be there - } else { /* Not changed (old) 0 */ - continue; // we didn't write the non changs line, so do not add a line break either - } - - $lc = \substr($diffEntry[0], -1); - if ($lc !== "\n" && $lc !== "\r") { - \fwrite($buffer, "\n"); // \No newline at end of file - } - } - - $diff = \stream_get_contents($buffer, -1, 0); - \fclose($buffer); - - return $diff; - } -} diff --git a/src/v2_0/Output/DiffOutputBuilderInterface.php b/src/v2_0/Output/DiffOutputBuilderInterface.php deleted file mode 100644 index b52b8ac8..00000000 --- a/src/v2_0/Output/DiffOutputBuilderInterface.php +++ /dev/null @@ -1,19 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PhpCsFixer\Diff\v2_0\Output; - -/** - * Defines how an output builder should take a generated - * diff array and return a string representation of that diff. - */ -interface DiffOutputBuilderInterface -{ - public function getDiff(array $diff); -} diff --git a/src/v2_0/Output/UnifiedDiffOutputBuilder.php b/src/v2_0/Output/UnifiedDiffOutputBuilder.php deleted file mode 100644 index e7618651..00000000 --- a/src/v2_0/Output/UnifiedDiffOutputBuilder.php +++ /dev/null @@ -1,165 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v2_0\Output; - -/** - * Builds a diff string representation in unified diff format in chunks. - */ -final class UnifiedDiffOutputBuilder extends AbstractChunkOutputBuilder -{ - /** - * @var string - */ - private $header; - - /** - * @var bool - */ - private $addLineNumbers; - - public function __construct($header = "--- Original\n+++ New\n", $addLineNumbers = false) - { - $this->header = $header; - $this->addLineNumbers = $addLineNumbers; - } - - public function getDiff(array $diff) - { - $buffer = \fopen('php://memory', 'r+b'); - - if ('' !== $this->header) { - \fwrite($buffer, $this->header); - if ("\n" !== \substr($this->header, -1, 1)) { - \fwrite($buffer, "\n"); - } - } - - $this->writeDiffChunked($buffer, $diff, $this->getCommonChunks($diff)); - - $diff = \stream_get_contents($buffer, -1, 0); - - \fclose($buffer); - - return $diff; - } - - // `old` is an array with key => value pairs . Each pair represents a start and end index of `diff` - // of a list of elements all containing `same` (0) entries. - private function writeDiffChunked($output, array $diff, array $old) - { - $upperLimit = \count($diff); - $start = 0; - $fromStart = 0; - $toStart = 0; - - if (\count($old)) { // no common parts, list all diff entries - \reset($old); - - // iterate the diff, go from chunk to chunk skipping common chunk of lines between those - do { - $commonStart = \key($old); - $commonEnd = \current($old); - - if ($commonStart !== $start) { - list($fromRange, $toRange) = $this->getChunkRange($diff, $start, $commonStart); - $this->writeChunk($output, $diff, $start, $commonStart, $fromStart, $fromRange, $toStart, $toRange); - - $fromStart += $fromRange; - $toStart += $toRange; - } - - $start = $commonEnd + 1; - $commonLength = $commonEnd - $commonStart + 1; // calculate number of non-change lines in the common part - $fromStart += $commonLength; - $toStart += $commonLength; - } while (false !== \next($old)); - - \end($old); // short cut for finding possible last `change entry` - $tmp = \key($old); - \reset($old); - if ($old[$tmp] === $upperLimit - 1) { - $upperLimit = $tmp; - } - } - - if ($start < $upperLimit - 1) { // check for trailing (non) diff entries - do { - --$upperLimit; - } while (isset($diff[$upperLimit][1]) && $diff[$upperLimit][1] === 0); - ++$upperLimit; - - list($fromRange, $toRange) = $this->getChunkRange($diff, $start, $upperLimit); - $this->writeChunk($output, $diff, $start, $upperLimit, $fromStart, $fromRange, $toStart, $toRange); - } - } - - private function writeChunk( - $output, - array $diff, - $diffStartIndex, - $diffEndIndex, - $fromStart, - $fromRange, - $toStart, - $toRange - ) { - if ($this->addLineNumbers) { - \fwrite($output, '@@ -' . (1 + $fromStart)); - - if ($fromRange !== 1) { - \fwrite($output, ',' . $fromRange); - } - - \fwrite($output, ' +' . (1 + $toStart)); - if ($toRange !== 1) { - \fwrite($output, ',' . $toRange); - } - - \fwrite($output, " @@\n"); - } else { - \fwrite($output, "@@ @@\n"); - } - - for ($i = $diffStartIndex; $i < $diffEndIndex; ++$i) { - if ($diff[$i][1] === 1 /* ADDED */) { - \fwrite($output, '+' . $diff[$i][0]); - } elseif ($diff[$i][1] === 2 /* REMOVED */) { - \fwrite($output, '-' . $diff[$i][0]); - } else { /* Not changed (old) 0 or Warning 3 */ - \fwrite($output, ' ' . $diff[$i][0]); - } - - $lc = \substr($diff[$i][0], -1); - if ($lc !== "\n" && $lc !== "\r") { - \fwrite($output, "\n"); // \No newline at end of file - } - } - } - - private function getChunkRange(array $diff, $diffStartIndex, $diffEndIndex) - { - $toRange = 0; - $fromRange = 0; - - for ($i = $diffStartIndex; $i < $diffEndIndex; ++$i) { - if ($diff[$i][1] === 1) { // added - ++$toRange; - } elseif ($diff[$i][1] === 2) { // removed - ++$fromRange; - } elseif ($diff[$i][1] === 0) { // same - ++$fromRange; - ++$toRange; - } - } - - return [$fromRange, $toRange]; - } -} diff --git a/src/v2_0/Parser.php b/src/v2_0/Parser.php deleted file mode 100644 index 9c0ee839..00000000 --- a/src/v2_0/Parser.php +++ /dev/null @@ -1,106 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v2_0; - -/** - * Unified diff parser. - */ -final class Parser -{ - /** - * @param string $string - * - * @return Diff[] - */ - public function parse($string) - { - $lines = \preg_split('(\r\n|\r|\n)', $string); - - if (!empty($lines) && $lines[\count($lines) - 1] === '') { - \array_pop($lines); - } - - $lineCount = \count($lines); - $diffs = []; - $diff = null; - $collected = []; - - for ($i = 0; $i < $lineCount; ++$i) { - if (\preg_match('(^---\\s+(?P\\S+))', $lines[$i], $fromMatch) && - \preg_match('(^\\+\\+\\+\\s+(?P\\S+))', $lines[$i + 1], $toMatch)) { - if ($diff !== null) { - $this->parseFileDiff($diff, $collected); - - $diffs[] = $diff; - $collected = []; - } - - $diff = new Diff($fromMatch['file'], $toMatch['file']); - - ++$i; - } else { - if (\preg_match('/^(?:diff --git |index [\da-f\.]+|[+-]{3} [ab])/', $lines[$i])) { - continue; - } - - $collected[] = $lines[$i]; - } - } - - if ($diff !== null && \count($collected)) { - $this->parseFileDiff($diff, $collected); - - $diffs[] = $diff; - } - - return $diffs; - } - - private function parseFileDiff(Diff $diff, array $lines) - { - $chunks = []; - $chunk = null; - - foreach ($lines as $line) { - if (\preg_match('/^@@\s+-(?P\d+)(?:,\s*(?P\d+))?\s+\+(?P\d+)(?:,\s*(?P\d+))?\s+@@/', $line, $match)) { - $chunk = new Chunk( - (int) $match['start'], - isset($match['startrange']) ? \max(1, (int) $match['startrange']) : 1, - (int) $match['end'], - isset($match['endrange']) ? \max(1, (int) $match['endrange']) : 1 - ); - - $chunks[] = $chunk; - $diffLines = []; - - continue; - } - - if (\preg_match('/^(?P[+ -])?(?P.*)/', $line, $match)) { - $type = Line::UNCHANGED; - - if ($match['type'] === '+') { - $type = Line::ADDED; - } elseif ($match['type'] === '-') { - $type = Line::REMOVED; - } - - $diffLines[] = new Line($type, $match['line']); - - if (null !== $chunk) { - $chunk->setLines($diffLines); - } - } - } - - $diff->setChunks($chunks); - } -} diff --git a/src/v2_0/TimeEfficientLongestCommonSubsequenceCalculator.php b/src/v2_0/TimeEfficientLongestCommonSubsequenceCalculator.php deleted file mode 100644 index 51489afa..00000000 --- a/src/v2_0/TimeEfficientLongestCommonSubsequenceCalculator.php +++ /dev/null @@ -1,66 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v2_0; - -final class TimeEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator -{ - /** - * {@inheritdoc} - */ - public function calculate(array $from, array $to) - { - $common = []; - $fromLength = \count($from); - $toLength = \count($to); - $width = $fromLength + 1; - $matrix = new \SplFixedArray($width * ($toLength + 1)); - - for ($i = 0; $i <= $fromLength; ++$i) { - $matrix[$i] = 0; - } - - for ($j = 0; $j <= $toLength; ++$j) { - $matrix[$j * $width] = 0; - } - - for ($i = 1; $i <= $fromLength; ++$i) { - for ($j = 1; $j <= $toLength; ++$j) { - $o = ($j * $width) + $i; - $matrix[$o] = \max( - $matrix[$o - 1], - $matrix[$o - $width], - $from[$i - 1] === $to[$j - 1] ? $matrix[$o - $width - 1] + 1 : 0 - ); - } - } - - $i = $fromLength; - $j = $toLength; - - while ($i > 0 && $j > 0) { - if ($from[$i - 1] === $to[$j - 1]) { - $common[] = $from[$i - 1]; - --$i; - --$j; - } else { - $o = ($j * $width) + $i; - - if ($matrix[$o - $width] > $matrix[$o - 1]) { - --$j; - } else { - --$i; - } - } - } - - return \array_reverse($common); - } -} diff --git a/src/v3_0/Exception/Exception.php b/src/v3_0/Exception/Exception.php deleted file mode 100644 index 7a391db7..00000000 --- a/src/v3_0/Exception/Exception.php +++ /dev/null @@ -1,15 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v3_0; - -interface Exception -{ -} diff --git a/src/v3_0/Output/AbstractChunkOutputBuilder.php b/src/v3_0/Output/AbstractChunkOutputBuilder.php deleted file mode 100644 index ecf7d321..00000000 --- a/src/v3_0/Output/AbstractChunkOutputBuilder.php +++ /dev/null @@ -1,56 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v3_0\Output; - -abstract class AbstractChunkOutputBuilder implements DiffOutputBuilderInterface -{ - /** - * Takes input of the diff array and returns the common parts. - * Iterates through diff line by line. - * - * @param array $diff - * @param int $lineThreshold - * - * @return array - */ - protected function getCommonChunks(array $diff, $lineThreshold = 5) - { - $diffSize = \count($diff); - $capturing = false; - $chunkStart = 0; - $chunkSize = 0; - $commonChunks = []; - - for ($i = 0; $i < $diffSize; ++$i) { - if ($diff[$i][1] === 0 /* OLD */) { - if ($capturing === false) { - $capturing = true; - $chunkStart = $i; - $chunkSize = 0; - } else { - ++$chunkSize; - } - } elseif ($capturing !== false) { - if ($chunkSize >= $lineThreshold) { - $commonChunks[$chunkStart] = $chunkStart + $chunkSize; - } - - $capturing = false; - } - } - - if ($capturing !== false && $chunkSize >= $lineThreshold) { - $commonChunks[$chunkStart] = $chunkStart + $chunkSize; - } - - return $commonChunks; - } -} diff --git a/tests/v3_0/ChunkTest.php b/tests/ChunkTest.php similarity index 95% rename from tests/v3_0/ChunkTest.php rename to tests/ChunkTest.php index 416c4a62..29fec466 100644 --- a/tests/v3_0/ChunkTest.php +++ b/tests/ChunkTest.php @@ -8,12 +8,12 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0; +namespace PhpCsFixer\Diff; use PHPUnit\Framework\TestCase; /** - * @covers PhpCsFixer\Diff\v3_0\Chunk + * @covers PhpCsFixer\Diff\Chunk */ final class ChunkTest extends TestCase { diff --git a/tests/v3_0/DiffTest.php b/tests/DiffTest.php similarity index 93% rename from tests/v3_0/DiffTest.php rename to tests/DiffTest.php index 7cc658e1..d7a46f28 100644 --- a/tests/v3_0/DiffTest.php +++ b/tests/DiffTest.php @@ -8,14 +8,14 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0; +namespace PhpCsFixer\Diff; use PHPUnit\Framework\TestCase; /** - * @covers PhpCsFixer\Diff\v3_0\Diff + * @covers PhpCsFixer\Diff\Diff * - * @uses PhpCsFixer\Diff\v3_0\Chunk + * @uses PhpCsFixer\Diff\Chunk */ final class DiffTest extends TestCase { diff --git a/tests/v3_0/DifferTest.php b/tests/DifferTest.php similarity index 96% rename from tests/v3_0/DifferTest.php rename to tests/DifferTest.php index 86c5103f..56906a9a 100644 --- a/tests/v3_0/DifferTest.php +++ b/tests/DifferTest.php @@ -8,18 +8,18 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0; +namespace PhpCsFixer\Diff; use PHPUnit\Framework\TestCase; -use PhpCsFixer\Diff\v3_0\Output\UnifiedDiffOutputBuilder; +use PhpCsFixer\Diff\Output\UnifiedDiffOutputBuilder; /** - * @covers PhpCsFixer\Diff\v3_0\Differ - * @covers PhpCsFixer\Diff\v3_0\Output\UnifiedDiffOutputBuilder + * @covers PhpCsFixer\Diff\Differ + * @covers PhpCsFixer\Diff\Output\UnifiedDiffOutputBuilder * - * @uses PhpCsFixer\Diff\v3_0\MemoryEfficientLongestCommonSubsequenceCalculator - * @uses PhpCsFixer\Diff\v3_0\TimeEfficientLongestCommonSubsequenceCalculator - * @uses PhpCsFixer\Diff\v3_0\Output\AbstractChunkOutputBuilder + * @uses PhpCsFixer\Diff\MemoryEfficientLongestCommonSubsequenceCalculator + * @uses PhpCsFixer\Diff\TimeEfficientLongestCommonSubsequenceCalculator + * @uses PhpCsFixer\Diff\Output\AbstractChunkOutputBuilder */ final class DifferTest extends TestCase { diff --git a/tests/v3_0/Exception/ConfigurationExceptionTest.php b/tests/Exception/ConfigurationExceptionTest.php similarity index 91% rename from tests/v3_0/Exception/ConfigurationExceptionTest.php rename to tests/Exception/ConfigurationExceptionTest.php index 63bfa572..a60f01a5 100644 --- a/tests/v3_0/Exception/ConfigurationExceptionTest.php +++ b/tests/Exception/ConfigurationExceptionTest.php @@ -8,12 +8,12 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0; +namespace PhpCsFixer\Diff; use PHPUnit\Framework\TestCase; /** - * @covers PhpCsFixer\Diff\v3_0\ConfigurationException + * @covers PhpCsFixer\Diff\ConfigurationException */ final class ConfigurationExceptionTest extends TestCase { diff --git a/tests/v3_0/Exception/InvalidArgumentExceptionTest.php b/tests/Exception/InvalidArgumentExceptionTest.php similarity index 90% rename from tests/v3_0/Exception/InvalidArgumentExceptionTest.php rename to tests/Exception/InvalidArgumentExceptionTest.php index 6bce2b63..bb8ce7cc 100644 --- a/tests/v3_0/Exception/InvalidArgumentExceptionTest.php +++ b/tests/Exception/InvalidArgumentExceptionTest.php @@ -8,12 +8,12 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0; +namespace PhpCsFixer\Diff; use PHPUnit\Framework\TestCase; /** - * @covers PhpCsFixer\Diff\v3_0\InvalidArgumentException + * @covers PhpCsFixer\Diff\InvalidArgumentException */ final class InvalidArgumentExceptionTest extends TestCase { diff --git a/tests/GeckoPackages/DiffOutputBuilder/Tests/AbstractDiffOutputBuilderTest.php b/tests/GeckoPackages/DiffOutputBuilder/Tests/AbstractDiffOutputBuilderTest.php deleted file mode 100644 index ad669e0c..00000000 --- a/tests/GeckoPackages/DiffOutputBuilder/Tests/AbstractDiffOutputBuilderTest.php +++ /dev/null @@ -1,97 +0,0 @@ -getDiffer($options)->diff($from, $to); - - $this->assertValidDiffFormat($diff); - $this->assertSame($expected, $diff); - } - - /** - * @param string $expected - * @param string $from - * @param string $to - * @param array $options - * - * @dataProvider provideSample - */ - public function testSample($expected, $from, $to, array $options) - { - $diff = $this->getDiffer($options)->diff($from, $to); - - $this->assertValidDiffFormat($diff); - $this->assertSame($expected, $diff); - } - - /** - * Returns a new instance of a Differ with a new instance of the class (DiffOutputBuilderInterface) under test. - * - * @param array $options - * - * @return Differ - */ - protected function getDiffer(array $options = []) - { - if (null === $this->differClass) { - // map test class name (child) back to the Differ being tested. - $childClass = \get_class($this); - $differClass = 'PhpCsFixer\\Diff\\GeckoPackages\\DiffOutputBuilder\\'.\substr($childClass, \strrpos($childClass, '\\') + 1, -4); - - // basic tests: class must exist... - $this->assertTrue(\class_exists($differClass)); - - // ...and must implement DiffOutputBuilderInterface - $implements = \class_implements($differClass); - $this->assertInternalType('array', $implements); - $this->assertArrayHasKey(DiffOutputBuilderInterface::class, $implements); - - $this->differClass = $differClass; - } - - return new Differ(new $this->differClass($options)); - } -} diff --git a/tests/GeckoPackages/DiffOutputBuilder/Tests/ConfigurationExceptionTest.php b/tests/GeckoPackages/DiffOutputBuilder/Tests/ConfigurationExceptionTest.php deleted file mode 100644 index 1e04846b..00000000 --- a/tests/GeckoPackages/DiffOutputBuilder/Tests/ConfigurationExceptionTest.php +++ /dev/null @@ -1,32 +0,0 @@ -assertSame(0, $e->getCode()); - $this->assertNull($e->getPrevious()); - $this->assertSame('Option "test" must be A, got "string#B".', $e->getMessage()); - } -} diff --git a/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/UnifiedDiffAssertTraitIntegrationTest.php b/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/UnifiedDiffAssertTraitIntegrationTest.php deleted file mode 100644 index a1e7aefa..00000000 --- a/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/UnifiedDiffAssertTraitIntegrationTest.php +++ /dev/null @@ -1,135 +0,0 @@ -filePatch = __DIR__.'/out/patch.txt'; - - $this->cleanUpTempFiles(); - } - - /** - * @param string $fileFrom - * @param string $fileTo - * - * @dataProvider provideFilePairsCases - */ - public function testValidPatches($fileFrom, $fileTo) - { - $command = \sprintf( - 'diff -u %s %s > %s', - \escapeshellarg(\realpath($fileFrom)), - \escapeshellarg(\realpath($fileTo)), - \escapeshellarg($this->filePatch) - ); - - $p = new Process($command); - $p->run(); - - $exitCode = $p->getExitCode(); - - if (0 === $exitCode) { - // odd case when two files have the same content. Test after executing as it is more efficient than to read the files and check the contents every time. - $this->addToAssertionCount(1); - - return; - } - - $this->assertSame( - 1, // means `diff` found a diff between the files we gave it - $exitCode, - \sprintf( - "Command exec. was not successful:\n\"%s\"\nOutput:\n\"%s\"\nStdErr:\n\"%s\"\nExit code %d.\n", - $command, - $p->getOutput(), - $p->getErrorOutput(), - $p->getExitCode() - ) - ); - - $this->assertValidUnifiedDiffFormat(\file_get_contents($this->filePatch)); - } - - /** - * @return array> - */ - public function provideFilePairsCases() - { - $cases = []; - - // created cases based on dedicated fixtures - $dir = \realpath(__DIR__.'/fixtures/UnifiedDiffAssertTraitIntegrationTest'); - $dirLength = \strlen($dir); - - for ($i = 1;; ++$i) { - $fromFile = \sprintf('%s/%d_a.txt', $dir, $i); - $toFile = \sprintf('%s/%d_b.txt', $dir, $i); - - if (!\file_exists($fromFile)) { - break; - } - - $this->assertFileExists($toFile); - $cases[\sprintf("Diff file:\n\"%s\"\nvs.\n\"%s\"\n", \substr(\realpath($fromFile), $dirLength), \substr(\realpath($toFile), $dirLength))] = [$fromFile, $toFile]; - } - - // create cases based on PHP files within the vendor directory for integration testing - $dir = \realpath(__DIR__.'/../../../../../vendor'); - $dirLength = \strlen($dir); - - $fileIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS)); - $fromFile = __FILE__; - - /** @var \SplFileInfo $file */ - foreach ($fileIterator as $file) { - if ('php' !== $file->getExtension()) { - continue; - } - - $toFile = $file->getPathname(); - $cases[\sprintf("Diff file:\n\"%s\"\nvs.\n\"%s\"\n", \substr(\realpath($fromFile), $dirLength), \substr(\realpath($toFile), $dirLength))] = [$fromFile, $toFile]; - $fromFile = $toFile; - } - - return $cases; - } - - protected function tearDown() - { - $this->cleanUpTempFiles(); - } - - private function cleanUpTempFiles() - { - @\unlink($this->filePatch); - } -} diff --git a/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/UnifiedDiffOutputBuilderIntegrationTest.php b/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/UnifiedDiffOutputBuilderIntegrationTest.php deleted file mode 100644 index 4fe3a89c..00000000 --- a/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/UnifiedDiffOutputBuilderIntegrationTest.php +++ /dev/null @@ -1,247 +0,0 @@ -dir = __DIR__.'/out/'; - $this->fileFrom = $this->dir.'from.txt'; - $this->fileTo = $this->dir.'to.txt'; - $this->filePatch = $this->dir.'diff.patch'; - - $this->cleanUpTempFiles(); - } - - /** - * Integration test - * - * - get a file pair - * - create a `diff` between the files - * - test applying the diff using `git apply` - * - test applying the diff using `patch` - * - * @param string $fileFrom - * @param string $fileTo - * - * @dataProvider provideFilePairs - */ - public function testIntegrationUsingPHPFileInVendor($fileFrom, $fileTo) - { - $from = @\file_get_contents($fileFrom); - $this->assertInternalType('string', $from, \sprintf('Failed to read file "%s".', $fileFrom)); - - $to = @\file_get_contents($fileTo); - $this->assertInternalType('string', $to, \sprintf('Failed to read file "%s".', $fileTo)); - - $diff = (new Differ(new UnifiedDiffOutputBuilder(['fromFile' => 'Original', 'toFile' => 'New'])))->diff($from, $to); - - if ('' === $diff && $from === $to) { - // odd case: test after executing as it is more efficient than to read the files and check the contents every time - $this->addToAssertionCount(1); - - return; - } - - $this->assertNotSame('', $diff); - $this->assertValidUnifiedDiffFormat($diff); - $this->doIntegrationTest($diff, $from, $to); - } - - /** - * @param string $expected - * @param string $from - * @param string $to - * - * @dataProvider provideOutputBuildingCases - * @dataProvider provideSample - * @dataProvider provideBasicDiffGeneration - */ - public function testIntegrationOfUnitTestCases($expected, $from, $to) - { - $this->doIntegrationTest($expected, $from, $to); - } - - public function provideOutputBuildingCases() - { - return UnifiedDiffOutputBuilderDataProvider::provideOutputBuildingCases(); - } - - public function provideSample() - { - return UnifiedDiffOutputBuilderDataProvider::provideSample(); - } - - public function provideBasicDiffGeneration() - { - return UnifiedDiffOutputBuilderDataProvider::provideBasicDiffGeneration(); - } - - public function provideFilePairs() - { - $cases = []; - $fromFile = __FILE__; - $vendorDir = \realpath(__DIR__.'/../../../../../vendor'); - $vendorDirLength = \strlen($vendorDir); - $fileIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($vendorDir, \RecursiveDirectoryIterator::SKIP_DOTS)); - - /** @var \SplFileInfo $file */ - foreach ($fileIterator as $file) { - if ('php' !== $file->getExtension()) { - continue; - } - - $toFile = $file->getPathname(); - $cases[\sprintf("Diff file:\n\"%s\"\nvs.\n\"%s\"\n", \substr(\realpath($fromFile), $vendorDirLength), \substr(\realpath($toFile), $vendorDirLength))] = [$fromFile, $toFile]; - $fromFile = $toFile; - } - - return $cases; - } - - /** - * Compare diff create by builder and against one create by `diff` command. - * - * @param string $diff - * @param string $from - * @param string $to - * - * @dataProvider provideBasicDiffGeneration - */ - public function testIntegrationDiffOutputBuilderVersusDiffCommand($diff, $from, $to) - { - $this->assertNotFalse(\file_put_contents($this->fileFrom, $from)); - $this->assertNotFalse(\file_put_contents($this->fileTo, $to)); - - $p = new Process(\sprintf('diff -u %s %s', \escapeshellarg($this->fileFrom), \escapeshellarg($this->fileTo))); - $p->run(); - $this->assertSame(1, $p->getExitCode()); - - $output = $p->getOutput(); - - $diffLines = \preg_split('/(.*\R)/', $diff, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); - $diffLines[0] = \preg_replace('#^\-\-\- .*#', '--- /'.$this->fileFrom, $diffLines[0], 1); - $diffLines[1] = \preg_replace('#^\+\+\+ .*#', '+++ /'.$this->fileFrom, $diffLines[1], 1); - $diff = \implode('', $diffLines); - - $outputLines = \preg_split('/(.*\R)/', $output, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); - $outputLines[0] = \preg_replace('#^\-\-\- .*#', '--- /'.$this->fileFrom, $outputLines[0], 1); - $outputLines[1] = \preg_replace('#^\+\+\+ .*#', '+++ /'.$this->fileFrom, $outputLines[1], 1); - $output = \implode('', $outputLines); - - $this->assertSame($diff, $output); - } - - private function doIntegrationTest($diff, $from, $to) - { - if ('' === $diff) { - $this->addToAssertionCount(1); // Empty diff has no integration test part. - - return; - } - - $diffLines = \preg_split('/(.*\R)/', $diff, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); - $diffLines[0] = \preg_replace('#^\-\-\- .*#', '--- /'.$this->fileFrom, $diffLines[0], 1); - $diffLines[1] = \preg_replace('#^\+\+\+ .*#', '+++ /'.$this->fileFrom, $diffLines[1], 1); - $diff = \implode('', $diffLines); - - $this->assertNotFalse(\file_put_contents($this->fileFrom, $from)); - $this->assertNotFalse(\file_put_contents($this->filePatch, $diff)); - - $command = \sprintf( - 'git --git-dir %s apply --check -v --unsafe-paths %s', // --unidiff-zero --ignore-whitespace - \escapeshellarg($this->dir), - \escapeshellarg($this->filePatch) - ); - - $p = new Process($command); - $p->run(); - - $this->assertTrue( - $p->isSuccessful(), - \sprintf( - "Command exec. was not successful:\n\"%s\"\nOutput:\n\"%s\"\nStdErr:\n\"%s\"\nExit code %d.\n", - $command, - $p->getOutput(), - $p->getErrorOutput(), - $p->getExitCode() - ) - ); - - $command = \sprintf( - 'patch -u --verbose --posix %s < %s', - \escapeshellarg($this->fileFrom), - \escapeshellarg($this->filePatch) - ); - - $p = new Process($command); - $p->run(); - - $output = $p->getOutput(); - - $this->assertTrue( - $p->isSuccessful(), - \sprintf( - "Command exec. was not successful:\n\"%s\"\nOutput:\n\"%s\"\nStdErr:\n\"%s\"\nExit code %d.\n", - $command, - $output, - $p->getErrorOutput(), - $p->getExitCode() - ) - ); - - $this->assertStringEqualsFile( - $this->fileFrom, $to, - \sprintf('Patch command "%s".', $command) - ); - } - - protected function tearDown() - { - $this->cleanUpTempFiles(); - } - - private function cleanUpTempFiles() - { - @\unlink($this->fileFrom.'.orig'); - @\unlink($this->fileFrom.'.rej'); - @\unlink($this->fileFrom); - @\unlink($this->fileTo); - @\unlink($this->filePatch); - } -} diff --git a/tests/GeckoPackages/DiffOutputBuilder/Tests/UnifiedDiffAssertTraitTest.php b/tests/GeckoPackages/DiffOutputBuilder/Tests/UnifiedDiffAssertTraitTest.php deleted file mode 100644 index 764b9e2c..00000000 --- a/tests/GeckoPackages/DiffOutputBuilder/Tests/UnifiedDiffAssertTraitTest.php +++ /dev/null @@ -1,419 +0,0 @@ -assertValidUnifiedDiffFormat($diff); - } - - public function provideValidCases() - { - return [ - [ -'--- Original -+++ New -@@ -8 +8 @@ --Z -+U -', - ], - [ -'--- Original -+++ New -@@ -8 +8 @@ --Z -+U -@@ -15 +15 @@ --X -+V -', - ], - 'empty diff. is valid' => [ - '', - ], - ]; - } - - public function testNoLinebreakEnd() - { - $this->expectException(\UnexpectedValueException::class); - $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Expected diff to end with a line break, got "C".', '#'))); - - $this->assertValidUnifiedDiffFormat("A\nB\nC"); - } - - public function testInvalidStartWithoutHeader() - { - $this->expectException(\UnexpectedValueException::class); - $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote("Expected line to start with '@', '-' or '+', got \"A\n\". Line 1.", '#'))); - - $this->assertValidUnifiedDiffFormat("A\n"); - } - - public function testInvalidStartHeader1() - { - $this->expectException(\UnexpectedValueException::class); - $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote("Line 1 indicates a header, so line 2 must start with \"+++\".\nLine 1: \"--- A\n\"\nLine 2: \"+ 1\n\".", '#'))); - - $this->assertValidUnifiedDiffFormat("--- A\n+ 1\n"); - } - - public function testInvalidStartHeader2() - { - $this->expectException(\UnexpectedValueException::class); - $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote("Header line does not match expected pattern, got \"+++ file X\n\". Line 2.", '#'))); - - $this->assertValidUnifiedDiffFormat("--- A\n+++ file\tX\n"); - } - - public function testInvalidStartHeader3() - { - $this->expectException(\UnexpectedValueException::class); - $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Date of header line does not match expected pattern, got "[invalid date]". Line 1.', '#'))); - - $this->assertValidUnifiedDiffFormat( -"--- Original\t[invalid date] -+++ New -@@ -1,2 +1,2 @@ --A -+B - ".' -'); - } - - public function testInvalidStartHeader4() - { - $this->expectException(\UnexpectedValueException::class); - $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote("Expected header line to start with \"+++ \", got \"+++INVALID\n\". Line 2.", '#'))); - - $this->assertValidUnifiedDiffFormat( -'--- Original -+++INVALID -@@ -1,2 +1,2 @@ --A -+B - '.' -'); - } - - public function testInvalidLine1() - { - $this->expectException(\UnexpectedValueException::class); - $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote("Expected line to start with '@', '-' or '+', got \"1\n\". Line 5.", '#'))); - - $this->assertValidUnifiedDiffFormat( -'--- Original -+++ New -@@ -8 +8 @@ --Z -1 -+U -'); - } - - public function testInvalidLine2() - { - $this->expectException(\UnexpectedValueException::class); - $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Expected string length of minimal 2, got 1. Line 4.', '#'))); - - $this->assertValidUnifiedDiffFormat( -'--- Original -+++ New -@@ -8 +8 @@ - - -'); - } - - public function testHunkInvalidFormat() - { - $this->expectException(\UnexpectedValueException::class); - $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote("Hunk header line does not match expected pattern, got \"@@ INVALID -1,1 +1,1 @@\n\". Line 3.", '#'))); - - $this->assertValidUnifiedDiffFormat( -'--- Original -+++ New -@@ INVALID -1,1 +1,1 @@ --Z -+U -'); - } - - public function testHunkOverlapFrom() - { - $this->expectException(\UnexpectedValueException::class); - $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected new hunk; "from" (\'-\') start overlaps previous hunk. Line 6.', '#'))); - - $this->assertValidUnifiedDiffFormat( -'--- Original -+++ New -@@ -8,1 +8,1 @@ --Z -+U -@@ -7,1 +9,1 @@ --Z -+U -'); - } - - public function testHunkOverlapTo() - { - $this->expectException(\UnexpectedValueException::class); - $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected new hunk; "to" (\'+\') start overlaps previous hunk. Line 6.', '#'))); - - $this->assertValidUnifiedDiffFormat( -'--- Original -+++ New -@@ -8,1 +8,1 @@ --Z -+U -@@ -17,1 +7,1 @@ --Z -+U -'); - } - - public function testExpectHunk1() - { - $this->expectException(\UnexpectedValueException::class); - $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Expected hunk start (\'@\'), got "+". Line 6.', '#'))); - - $this->assertValidUnifiedDiffFormat( -'--- Original -+++ New -@@ -8 +8 @@ --Z -+U -+O -'); - } - - public function testExpectHunk2() - { - $this->expectException(\UnexpectedValueException::class); - $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected hunk start (\'@\'). Line 6.', '#'))); - - $this->assertValidUnifiedDiffFormat( -'--- Original -+++ New -@@ -8,12 +8,12 @@ - '.' - '.' -@@ -38,12 +48,12 @@ -'); - } - - public function testMisplacedLineAfterComments1() - { - $this->expectException(\UnexpectedValueException::class); - $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected line as 2 "No newline" markers have found, ". Line 8.', '#'))); - - $this->assertValidUnifiedDiffFormat( -'--- Original -+++ New -@@ -8 +8 @@ --Z -\ No newline at end of file -+U -\ No newline at end of file -+A -'); - } - - public function testMisplacedLineAfterComments2() - { - $this->expectException(\UnexpectedValueException::class); - $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected line as 2 "No newline" markers have found, ". Line 7.', '#'))); - - $this->assertValidUnifiedDiffFormat( -'--- Original -+++ New -@@ -8 +8 @@ -+U -\ No newline at end of file -\ No newline at end of file -\ No newline at end of file -'); - } - - public function testMisplacedLineAfterComments3() - { - $this->expectException(\UnexpectedValueException::class); - $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected line as 2 "No newline" markers have found, ". Line 7.', '#'))); - - $this->assertValidUnifiedDiffFormat( -'--- Original -+++ New -@@ -8 +8 @@ -+U -\ No newline at end of file -\ No newline at end of file -+A -'); - } - - public function testMisplacedComment() - { - $this->expectException(\UnexpectedValueException::class); - $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected "\ No newline at end of file", it must be preceded by \'+\' or \'-\' line. Line 1.', '#'))); - - $this->assertValidUnifiedDiffFormat( -'\ No newline at end of file -'); - } - - public function testUnexpectedDuplicateNoNewLineEOF() - { - $this->expectException(\UnexpectedValueException::class); - $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected "\\ No newline at end of file", "\\" was already closed. Line 8.', '#'))); - - $this->assertValidUnifiedDiffFormat( -'--- Original -+++ New -@@ -8,12 +8,12 @@ - '.' - '.' -\ No newline at end of file - '.' -\ No newline at end of file -'); - } - - public function testFromAfterClose() - { - $this->expectException(\UnexpectedValueException::class); - $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Not expected from (\'-\'), already closed by "\ No newline at end of file". Line 6.', '#'))); - - $this->assertValidUnifiedDiffFormat( -'--- Original -+++ New -@@ -8,12 +8,12 @@ --A -\ No newline at end of file --A -\ No newline at end of file -'); - } - - public function testSameAfterFromClose() - { - $this->expectException(\UnexpectedValueException::class); - $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Not expected same (\' \'), \'-\' already closed by "\ No newline at end of file". Line 6.', '#'))); - - $this->assertValidUnifiedDiffFormat( - '--- Original -+++ New -@@ -8,12 +8,12 @@ --A -\ No newline at end of file - A -\ No newline at end of file -'); - } - - public function testToAfterClose() - { - $this->expectException(\UnexpectedValueException::class); - $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Not expected to (\'+\'), already closed by "\ No newline at end of file". Line 6.', '#'))); - - $this->assertValidUnifiedDiffFormat( - '--- Original -+++ New -@@ -8,12 +8,12 @@ -+A -\ No newline at end of file -+A -\ No newline at end of file -'); - } - - public function testSameAfterToClose() - { - $this->expectException(\UnexpectedValueException::class); - $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Not expected same (\' \'), \'+\' already closed by "\ No newline at end of file". Line 6.', '#'))); - - $this->assertValidUnifiedDiffFormat( - '--- Original -+++ New -@@ -8,12 +8,12 @@ -+A -\ No newline at end of file - A -\ No newline at end of file -'); - } - - public function testUnexpectedEOFFromMissingLines() - { - $this->expectException(\UnexpectedValueException::class); - $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected EOF, number of lines in hunk "from" (\'-\')) mismatched. Line 7.', '#'))); - - $this->assertValidUnifiedDiffFormat( -'--- Original -+++ New -@@ -8,19 +7,2 @@ --A -+B - '.' -'); - } - - public function testUnexpectedEOFToMissingLines() - { - $this->expectException(\UnexpectedValueException::class); - $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected EOF, number of lines in hunk "to" (\'+\')) mismatched. Line 7.', '#'))); - - $this->assertValidUnifiedDiffFormat( -'--- Original -+++ New -@@ -8,2 +7,3 @@ --A -+B - '.' -'); - } - - public function testUnexpectedEOFBothFromAndToMissingLines() - { - $this->expectException(\UnexpectedValueException::class); - $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote('Unexpected EOF, number of lines in hunk "from" (\'-\')) and "to" (\'+\') mismatched. Line 7.', '#'))); - - $this->assertValidUnifiedDiffFormat( -'--- Original -+++ New -@@ -1,12 +1,14 @@ --A -+B - '.' -'); - } -} diff --git a/tests/GeckoPackages/DiffOutputBuilder/Tests/UnifiedDiffOutputBuilderDataProvider.php b/tests/GeckoPackages/DiffOutputBuilder/Tests/UnifiedDiffOutputBuilderDataProvider.php deleted file mode 100644 index b77c85c2..00000000 --- a/tests/GeckoPackages/DiffOutputBuilder/Tests/UnifiedDiffOutputBuilderDataProvider.php +++ /dev/null @@ -1,197 +0,0 @@ - 'input.txt', - 'toFile' => 'output.txt', - ], - ], - [ -'--- '.__FILE__."\t2017-10-02 17:38:11.586413675 +0100 -+++ output1.txt\t2017-10-03 12:09:43.086719482 +0100 -@@ -1,1 +1,1 @@ --B -+X -", - "B\n", - "X\n", - [ - 'fromFile' => __FILE__, - 'fromFileDate' => '2017-10-02 17:38:11.586413675 +0100', - 'toFile' => 'output1.txt', - 'toFileDate' => '2017-10-03 12:09:43.086719482 +0100', - 'collapseRanges' => false, - ], - ], - [ -'--- input.txt -+++ output.txt -@@ -1 +1 @@ --B -+X -', - "B\n", - "X\n", - [ - 'fromFile' => 'input.txt', - 'toFile' => 'output.txt', - 'collapseRanges' => true, - ], - ], - ]; - } - - public static function provideSample() - { - return [ - [ -'--- input.txt -+++ output.txt -@@ -1,6 +1,6 @@ - 1 - 2 - 3 --4 -+X - 5 - 6 -', - "1\n2\n3\n4\n5\n6\n", - "1\n2\n3\nX\n5\n6\n", - [ - 'fromFile' => 'input.txt', - 'toFile' => 'output.txt', - ], - ], - ]; - } - - public static function provideBasicDiffGeneration() - { - return [ - [ -"--- input.txt -+++ output.txt -@@ -1,2 +1 @@ --A --B -+A\rB -", - "A\nB\n", - "A\rB\n", - ], - [ -"--- input.txt -+++ output.txt -@@ -1 +1 @@ -- -+\r -\\ No newline at end of file -", - "\n", - "\r", - ], - [ -"--- input.txt -+++ output.txt -@@ -1 +1 @@ --\r -\\ No newline at end of file -+ -", - "\r", - "\n", - ], - [ -'--- input.txt -+++ output.txt -@@ -1,3 +1,3 @@ - X - A --A -+B -', - "X\nA\nA\n", - "X\nA\nB\n", - ], - [ -'--- input.txt -+++ output.txt -@@ -1,3 +1,3 @@ - X - A --A -\ No newline at end of file -+B -', - "X\nA\nA", - "X\nA\nB\n", - ], - [ -'--- input.txt -+++ output.txt -@@ -1,3 +1,3 @@ - A - A --A -+B -\ No newline at end of file -', - "A\nA\nA\n", - "A\nA\nB", - ], - [ -'--- input.txt -+++ output.txt -@@ -1 +1 @@ --A -\ No newline at end of file -+B -\ No newline at end of file -', - 'A', - 'B', - ], - ]; - } -} diff --git a/tests/GeckoPackages/DiffOutputBuilder/Tests/UnifiedDiffOutputBuilderTest.php b/tests/GeckoPackages/DiffOutputBuilder/Tests/UnifiedDiffOutputBuilderTest.php deleted file mode 100644 index f27c69f1..00000000 --- a/tests/GeckoPackages/DiffOutputBuilder/Tests/UnifiedDiffOutputBuilderTest.php +++ /dev/null @@ -1,386 +0,0 @@ -assertValidUnifiedDiffFormat($diff); - } - - /** - * {@inheritdoc} - */ - public function provideOutputBuildingCases() - { - return UnifiedDiffOutputBuilderDataProvider::provideOutputBuildingCases(); - } - - /** - * {@inheritdoc} - */ - public function provideSample() - { - return UnifiedDiffOutputBuilderDataProvider::provideSample(); - } - - /** - * @param string $expected - * @param string $from - * @param string $to - * - * @dataProvider provideBasicDiffGeneration - */ - public function testBasicDiffGeneration($expected, $from, $to) - { - $diff = $this->getDiffer([ - 'fromFile' => 'input.txt', - 'toFile' => 'output.txt', - ])->diff($from, $to); - - $this->assertValidDiffFormat($diff); - $this->assertSame($expected, $diff); - } - - public function provideBasicDiffGeneration() - { - return UnifiedDiffOutputBuilderDataProvider::provideBasicDiffGeneration(); - } - - /** - * @param string $expected - * @param string $from - * @param string $to - * @param array $config - * - * @dataProvider provideConfiguredDiffGeneration - */ - public function testConfiguredDiffGeneration($expected, $from, $to, array $config = []) - { - $diff = $this->getDiffer(\array_merge([ - 'fromFile' => 'input.txt', - 'toFile' => 'output.txt', - ], $config))->diff($from, $to); - - $this->assertValidDiffFormat($diff); - $this->assertSame($expected, $diff); - } - - public function provideConfiguredDiffGeneration() - { - return [ - [ - '', - "1\n2", - "1\n2", - ], - [ - '', - "1\n", - "1\n", - ], - [ -'--- input.txt -+++ output.txt -@@ -4 +4 @@ --X -+4 -', - "1\n2\n3\nX\n5\n6\n7\n8\n9\n0\n", - "1\n2\n3\n4\n5\n6\n7\n8\n9\n0\n", - [ - 'contextLines' => 0, - ], - ], - [ -'--- input.txt -+++ output.txt -@@ -3,3 +3,3 @@ - 3 --X -+4 - 5 -', - "1\n2\n3\nX\n5\n6\n7\n8\n9\n0\n", - "1\n2\n3\n4\n5\n6\n7\n8\n9\n0\n", - [ - 'contextLines' => 1, - ], - ], - [ -'--- input.txt -+++ output.txt -@@ -1,10 +1,10 @@ - 1 - 2 - 3 --X -+4 - 5 - 6 - 7 - 8 - 9 - 0 -', - "1\n2\n3\nX\n5\n6\n7\n8\n9\n0\n", - "1\n2\n3\n4\n5\n6\n7\n8\n9\n0\n", - [ - 'contextLines' => 999, - ], - ], - [ -'--- input.txt -+++ output.txt -@@ -1,0 +1,2 @@ -+ -+A -', - '', - "\nA\n", - ], - [ -'--- input.txt -+++ output.txt -@@ -1,2 +1,0 @@ -- --A -', - "\nA\n", - '', - ], - [ - '--- input.txt -+++ output.txt -@@ -1,5 +1,5 @@ - 1 --X -+2 - 3 --Y -+4 - 5 -@@ -8,3 +8,3 @@ - 8 --X -+9 - 0 -', - "1\nX\n3\nY\n5\n6\n7\n8\nX\n0\n", - "1\n2\n3\n4\n5\n6\n7\n8\n9\n0\n", - [ - 'commonLineThreshold' => 2, - 'contextLines' => 1, - ], - ], - [ - '--- input.txt -+++ output.txt -@@ -2 +2 @@ --X -+2 -@@ -4 +4 @@ --Y -+4 -@@ -9 +9 @@ --X -+9 -', - "1\nX\n3\nY\n5\n6\n7\n8\nX\n0\n", - "1\n2\n3\n4\n5\n6\n7\n8\n9\n0\n", - [ - 'commonLineThreshold' => 1, - 'contextLines' => 0, - ], - ], - ]; - } - - public function testReUseBuilder() - { - $differ = $this->getDiffer([ - 'fromFile' => 'input.txt', - 'toFile' => 'output.txt', - ]); - - $diff = $differ->diff("A\nB\n", "A\nX\n"); - $this->assertSame( -'--- input.txt -+++ output.txt -@@ -1,2 +1,2 @@ - A --B -+X -', - $diff - ); - - $diff = $differ->diff("A\n", "A\n"); - $this->assertSame( - '', - $diff - ); - } - - public function testEmptyDiff() - { - $builder = new UnifiedDiffOutputBuilder([ - 'fromFile' => 'input.txt', - 'toFile' => 'output.txt', - ]); - - $this->assertSame( - '', - $builder->getDiff([]) - ); - } - - /** - * @param array $options - * @param string $message - * - * @dataProvider provideInvalidConfiguration - */ - public function testInvalidConfiguration(array $options, $message) - { - $this->expectException(ConfigurationException::class); - $this->expectExceptionMessageRegExp(\sprintf('#^%s$#', \preg_quote($message, '#'))); - - new UnifiedDiffOutputBuilder($options); - } - - public function provideInvalidConfiguration() - { - $time = \time(); - - return [ - [ - ['collapseRanges' => 1], - 'Option "collapseRanges" must be a bool, got "integer#1".', - ], - [ - ['contextLines' => 'a'], - 'Option "contextLines" must be an int >= 0, got "string#a".', - ], - [ - ['commonLineThreshold' => -2], - 'Option "commonLineThreshold" must be an int > 0, got "integer#-2".', - ], - [ - ['commonLineThreshold' => 0], - 'Option "commonLineThreshold" must be an int > 0, got "integer#0".', - ], - [ - ['fromFile' => new \SplFileInfo(__FILE__)], - 'Option "fromFile" must be a string, got "SplFileInfo".', - ], - [ - ['fromFile' => null], - 'Option "fromFile" must be a string, got "".', - ], - [ - [ - 'fromFile' => __FILE__, - 'toFile' => 1, - ], - 'Option "toFile" must be a string, got "integer#1".', - ], - [ - [ - 'fromFile' => __FILE__, - 'toFile' => __FILE__, - 'toFileDate' => $time, - ], - 'Option "toFileDate" must be a string or , got "integer#'.$time.'".', - ], - [ - [], - 'Option "fromFile" must be a string, got "".', - ], - ]; - } - - /** - * @param string $expected - * @param string $from - * @param string $to - * @param int $threshold - * - * @dataProvider provideCommonLineThresholdCases - */ - public function testCommonLineThreshold($expected, $from, $to, $threshold) - { - $diff = $this->getDiffer([ - 'fromFile' => 'input.txt', - 'toFile' => 'output.txt', - 'commonLineThreshold' => $threshold, - 'contextLines' => 0, - ])->diff($from, $to); - - $this->assertValidDiffFormat($diff); - $this->assertSame($expected, $diff); - } - - public function provideCommonLineThresholdCases() - { - return [ - [ -'--- input.txt -+++ output.txt -@@ -2,3 +2,3 @@ --X -+B - C12 --Y -+D -@@ -7 +7 @@ --X -+Z -', - "A\nX\nC12\nY\nA\nA\nX\n", - "A\nB\nC12\nD\nA\nA\nZ\n", - 2, - ], - [ -'--- input.txt -+++ output.txt -@@ -2 +2 @@ --X -+B -@@ -4 +4 @@ --Y -+D -', - "A\nX\nV\nY\n", - "A\nB\nV\nD\n", - 1, - ], - ]; - } -} diff --git a/tests/GeckoPackages/DiffOutputBuilder/Utils/PHPUnitPolyfill.php b/tests/GeckoPackages/DiffOutputBuilder/Utils/PHPUnitPolyfill.php deleted file mode 100644 index 8e03414e..00000000 --- a/tests/GeckoPackages/DiffOutputBuilder/Utils/PHPUnitPolyfill.php +++ /dev/null @@ -1,49 +0,0 @@ -wellYeahShipIt('expectedException', $exception); - } - - public function expectExceptionMessageRegExp($messageRegExp) - { - if (\method_exists(TestCase::class, 'expectExceptionMessageRegExp')) { - return parent::expectExceptionMessageRegExp($messageRegExp); - } - - $this->wellYeahShipIt('expectedExceptionMessageRegExp', $messageRegExp); - } - - private function wellYeahShipIt($key, $value) - { - $self = new \ReflectionClass(PHPUnit_Framework_TestCase::class); - $property = $self->getProperty($key); - $property->setAccessible(true); - $property->setValue($this, $value); - } -} diff --git a/tests/GeckoPackages/DiffOutputBuilder/Utils/UnifiedDiffAssertTrait.php b/tests/GeckoPackages/DiffOutputBuilder/Utils/UnifiedDiffAssertTrait.php deleted file mode 100644 index 0a5d1e6f..00000000 --- a/tests/GeckoPackages/DiffOutputBuilder/Utils/UnifiedDiffAssertTrait.php +++ /dev/null @@ -1,279 +0,0 @@ -addToAssertionCount(1); - - return; - } - - // test diff ends with a line break - $last = \substr($diff, -1); - if ("\n" !== $last && "\r" !== $last) { - throw new \UnexpectedValueException(\sprintf('Expected diff to end with a line break, got "%s".', $last)); - } - - $lines = \preg_split('/(.*\R)/', $diff, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); - $lineCount = \count($lines); - $lineNumber = $diffLineFromNumber = $diffLineToNumber = 1; - $fromStart = $fromTillOffset = $toStart = $toTillOffset = -1; - $expectHunkHeader = true; - - // check for header - if ($lineCount > 1) { - $this->unifiedDiffAssertLinePrefix($lines[0], 'Line 1.'); - $this->unifiedDiffAssertLinePrefix($lines[1], 'Line 2.'); - - if ('---' === \substr($lines[0], 0, 3)) { - if ('+++' !== \substr($lines[1], 0, 3)) { - throw new \UnexpectedValueException(\sprintf("Line 1 indicates a header, so line 2 must start with \"+++\".\nLine 1: \"%s\"\nLine 2: \"%s\".", $lines[0], $lines[1])); - } - - $this->unifiedDiffAssertHeaderLine($lines[0], '--- ', 'Line 1.'); - $this->unifiedDiffAssertHeaderLine($lines[1], '+++ ', 'Line 2.'); - - $lineNumber = 3; - } - } - - $endOfLineTypes = []; - $diffClosed = false; - - // assert format of lines, get all hunks, test the line numbers - for (; $lineNumber <= $lineCount; ++$lineNumber) { - if ($diffClosed) { - throw new \UnexpectedValueException(\sprintf('Unexpected line as 2 "No newline" markers have found, ". Line %d.', $lineNumber)); - } - - $line = $lines[$lineNumber - 1]; // line numbers start by 1, array index at 0 - $type = $this->unifiedDiffAssertLinePrefix($line, \sprintf('Line %d.', $lineNumber)); - - if ($expectHunkHeader && '@' !== $type && '\\' !== $type) { - throw new \UnexpectedValueException(\sprintf('Expected hunk start (\'@\'), got "%s". Line %d.', $type, $lineNumber)); - } - - if ('@' === $type) { - if (!$expectHunkHeader) { - throw new \UnexpectedValueException(\sprintf('Unexpected hunk start (\'@\'). Line %d.', $lineNumber)); - } - - $previousHunkFromEnd = $fromStart + $fromTillOffset; - $previousHunkTillEnd = $toStart + $toTillOffset; - - list($fromStart, $fromTillOffset, $toStart, $toTillOffset) = $this->unifiedDiffAssertHunkHeader($line, \sprintf('Line %d.', $lineNumber)); - - // detect overlapping hunks - if ($fromStart < $previousHunkFromEnd) { - throw new \UnexpectedValueException(\sprintf('Unexpected new hunk; "from" (\'-\') start overlaps previous hunk. Line %d.', $lineNumber)); - } - - if ($toStart < $previousHunkTillEnd) { - throw new \UnexpectedValueException(\sprintf('Unexpected new hunk; "to" (\'+\') start overlaps previous hunk. Line %d.', $lineNumber)); - } - - /* valid states; hunks touches against each other: - $fromStart === $previousHunkFromEnd - $toStart === $previousHunkTillEnd - */ - - $diffLineFromNumber = $fromStart; - $diffLineToNumber = $toStart; - $expectHunkHeader = false; - - continue; - } - - if ('-' === $type) { - if (isset($endOfLineTypes['-'])) { - throw new \UnexpectedValueException(\sprintf('Not expected from (\'-\'), already closed by "\\ No newline at end of file". Line %d.', $lineNumber)); - } - - ++$diffLineFromNumber; - } elseif ('+' === $type) { - if (isset($endOfLineTypes['+'])) { - throw new \UnexpectedValueException(\sprintf('Not expected to (\'+\'), already closed by "\\ No newline at end of file". Line %d.', $lineNumber)); - } - - ++$diffLineToNumber; - } elseif (' ' === $type) { - if (isset($endOfLineTypes['-'])) { - throw new \UnexpectedValueException(\sprintf('Not expected same (\' \'), \'-\' already closed by "\\ No newline at end of file". Line %d.', $lineNumber)); - } - - if (isset($endOfLineTypes['+'])) { - throw new \UnexpectedValueException(\sprintf('Not expected same (\' \'), \'+\' already closed by "\\ No newline at end of file". Line %d.', $lineNumber)); - } - - ++$diffLineFromNumber; - ++$diffLineToNumber; - } elseif ('\\' === $type) { - if (!isset($lines[$lineNumber - 2])) { - throw new \UnexpectedValueException(\sprintf('Unexpected "\\ No newline at end of file", it must be preceded by \'+\' or \'-\' line. Line %d.', $lineNumber)); - } - - $previousType = $this->unifiedDiffAssertLinePrefix($lines[$lineNumber - 2], \sprintf('Preceding line of "\\ No newline at end of file" of unexpected format. Line %d.', $lineNumber)); - if (isset($endOfLineTypes[$previousType])) { - throw new \UnexpectedValueException(\sprintf('Unexpected "\\ No newline at end of file", "%s" was already closed. Line %d.', $type, $lineNumber)); - } - - $endOfLineTypes[$previousType] = true; - $diffClosed = \count($endOfLineTypes) > 1; - } else { - // internal state error - throw new \RuntimeException(\sprintf('Unexpected line type "%s" Line %d.', $type, $lineNumber)); - } - - $expectHunkHeader = - $diffLineFromNumber === ($fromStart + $fromTillOffset) - && $diffLineToNumber === ($toStart + $toTillOffset); - } - - if ( - $diffLineFromNumber !== ($fromStart + $fromTillOffset) - && $diffLineToNumber !== ($toStart + $toTillOffset) - ) { - throw new \UnexpectedValueException(\sprintf('Unexpected EOF, number of lines in hunk "from" (\'-\')) and "to" (\'+\') mismatched. Line %d.', $lineNumber)); - } - - if ($diffLineFromNumber !== ($fromStart + $fromTillOffset)) { - throw new \UnexpectedValueException(\sprintf('Unexpected EOF, number of lines in hunk "from" (\'-\')) mismatched. Line %d.', $lineNumber)); - } - - if ($diffLineToNumber !== ($toStart + $toTillOffset)) { - throw new \UnexpectedValueException(\sprintf('Unexpected EOF, number of lines in hunk "to" (\'+\')) mismatched. Line %d.', $lineNumber)); - } - - $this->addToAssertionCount(1); - } - - /** - * @param string $line - * @param string $message - * - * @return string '+', '-', '@', ' ' or '\' - */ - private function unifiedDiffAssertLinePrefix($line, $message) - { - $this->unifiedDiffAssertStrLength($line, 2, $message); // 2: line type indicator ('+', '-', ' ' or '\') and a line break - $firstChar = $line[0]; - - if ('+' === $firstChar || '-' === $firstChar || '@' === $firstChar || ' ' === $firstChar) { - return $firstChar; - } - - if ("\\ No newline at end of file\n" === $line) { - return '\\'; - } - - throw new \UnexpectedValueException(\sprintf('Expected line to start with \'@\', \'-\' or \'+\', got "%s". %s', $line, $message)); - } - - private function unifiedDiffAssertStrLength($line, $min, $message) - { - $length = \strlen($line); - if ($length < $min) { - throw new \UnexpectedValueException(\sprintf('Expected string length of minimal %d, got %d. %s', $min, $length, $message)); - } - } - - /** - * Assert valid unified diff header line - * - * Samples: - * - "+++ from1.txt\t2017-08-24 19:51:29.383985722 +0200" - * - "+++ from1.txt" - * - * @param string $line - * @param string $start - * @param string $message - */ - private function unifiedDiffAssertHeaderLine($line, $start, $message) - { - if (0 !== \strpos($line, $start)) { - throw new \UnexpectedValueException(\sprintf('Expected header line to start with "%s", got "%s". %s', $start.' ', $line, $message)); - } - - // sample "+++ from1.txt\t2017-08-24 19:51:29.383985722 +0200\n" - $match = \preg_match( - "/^([^\t]*)(?:[\t]([\\S].*[\\S]))?\n$/", - \substr($line, 4), // 4 === string length of "+++ " / "--- " - $matches - ); - - if (1 !== $match) { - throw new \UnexpectedValueException(\sprintf('Header line does not match expected pattern, got "%s". %s', $line, $message)); - } - - // $file = $matches[1]; - - if (\count($matches) > 2) { - $this->unifiedDiffAssertHeaderDate($matches[2], $message); - } - } - - private function unifiedDiffAssertHeaderDate($date, $message) - { - // sample "2017-08-24 19:51:29.383985722 +0200" - $match = \preg_match( - '/^([\d]{4})-([01]?[\d])-([0123]?[\d])(:? [\d]{1,2}:[\d]{1,2}(?::[\d]{1,2}(:?\.[\d]+)?)?(?: ([\+\-][\d]{4}))?)?$/', - $date, - $matches - ); - - if (1 !== $match || ($matchesCount = \count($matches)) < 4) { - throw new \UnexpectedValueException(\sprintf('Date of header line does not match expected pattern, got "%s". %s', $date, $message)); - } - - // [$full, $year, $month, $day, $time] = $matches; - } - - /** - * @param string $line - * @param string $message - * - * @return int[] - */ - private function unifiedDiffAssertHunkHeader($line, $message) - { - if (1 !== \preg_match('#^@@ -([\d]+)((?:,[\d]+)?) \+([\d]+)((?:,[\d]+)?) @@\n$#', $line, $matches)) { - throw new \UnexpectedValueException( - \sprintf( - 'Hunk header line does not match expected pattern, got "%s". %s', - $line, - $message - ) - ); - } - - return [ - (int) $matches[1], - empty($matches[2]) ? 1 : (int) \substr($matches[2], 1), - (int) $matches[3], - empty($matches[4]) ? 1 : (int) \substr($matches[4], 1), - ]; - } -} diff --git a/tests/v3_0/LineTest.php b/tests/LineTest.php similarity index 92% rename from tests/v3_0/LineTest.php rename to tests/LineTest.php index fe356c09..3e52b85e 100644 --- a/tests/v3_0/LineTest.php +++ b/tests/LineTest.php @@ -8,12 +8,12 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0; +namespace PhpCsFixer\Diff; use PHPUnit\Framework\TestCase; /** - * @covers PhpCsFixer\Diff\v3_0\Line + * @covers PhpCsFixer\Diff\Line */ final class LineTest extends TestCase { diff --git a/tests/v3_0/LongestCommonSubsequenceTest.php b/tests/LongestCommonSubsequenceTest.php similarity index 99% rename from tests/v3_0/LongestCommonSubsequenceTest.php rename to tests/LongestCommonSubsequenceTest.php index 2d074b51..47856642 100644 --- a/tests/v3_0/LongestCommonSubsequenceTest.php +++ b/tests/LongestCommonSubsequenceTest.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0; +namespace PhpCsFixer\Diff; use PHPUnit\Framework\TestCase; diff --git a/tests/v3_0/MemoryEfficientImplementationTest.php b/tests/MemoryEfficientImplementationTest.php similarity index 80% rename from tests/v3_0/MemoryEfficientImplementationTest.php rename to tests/MemoryEfficientImplementationTest.php index 41f1f2f5..26b18628 100644 --- a/tests/v3_0/MemoryEfficientImplementationTest.php +++ b/tests/MemoryEfficientImplementationTest.php @@ -8,10 +8,10 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0; +namespace PhpCsFixer\Diff; /** - * @covers PhpCsFixer\Diff\v3_0\MemoryEfficientLongestCommonSubsequenceCalculator + * @covers PhpCsFixer\Diff\MemoryEfficientLongestCommonSubsequenceCalculator */ final class MemoryEfficientImplementationTest extends LongestCommonSubsequenceTest { diff --git a/tests/v3_0/Output/AbstractChunkOutputBuilderTest.php b/tests/Output/AbstractChunkOutputBuilderTest.php similarity index 92% rename from tests/v3_0/Output/AbstractChunkOutputBuilderTest.php rename to tests/Output/AbstractChunkOutputBuilderTest.php index 3f905edf..9f7c453e 100644 --- a/tests/v3_0/Output/AbstractChunkOutputBuilderTest.php +++ b/tests/Output/AbstractChunkOutputBuilderTest.php @@ -8,10 +8,10 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0\Output; +namespace PhpCsFixer\Diff\Output; use PHPUnit\Framework\TestCase; -use PhpCsFixer\Diff\v3_0\Differ; +use PhpCsFixer\Diff\Differ; class TestingAbstractChunkOutputBuilder extends AbstractChunkOutputBuilder { public function getDiff(array $diff) @@ -26,11 +26,11 @@ public function getChunks(array $diff, $lineThreshold) }; /** - * @covers PhpCsFixer\Diff\v3_0\Output\AbstractChunkOutputBuilder + * @covers PhpCsFixer\Diff\Output\AbstractChunkOutputBuilder * - * @uses PhpCsFixer\Diff\v3_0\Differ - * @uses PhpCsFixer\Diff\v3_0\Output\UnifiedDiffOutputBuilder - * @uses PhpCsFixer\Diff\v3_0\TimeEfficientLongestCommonSubsequenceCalculator + * @uses PhpCsFixer\Diff\Differ + * @uses PhpCsFixer\Diff\Output\UnifiedDiffOutputBuilder + * @uses PhpCsFixer\Diff\TimeEfficientLongestCommonSubsequenceCalculator */ final class AbstractChunkOutputBuilderTest extends TestCase { diff --git a/tests/v3_0/Output/DiffOnlyOutputBuilderTest.php b/tests/Output/DiffOnlyOutputBuilderTest.php similarity index 86% rename from tests/v3_0/Output/DiffOnlyOutputBuilderTest.php rename to tests/Output/DiffOnlyOutputBuilderTest.php index 9fd0cbee..799b8f2d 100644 --- a/tests/v3_0/Output/DiffOnlyOutputBuilderTest.php +++ b/tests/Output/DiffOnlyOutputBuilderTest.php @@ -8,16 +8,16 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0\Output; +namespace PhpCsFixer\Diff\Output; use PHPUnit\Framework\TestCase; -use PhpCsFixer\Diff\v3_0\Differ; +use PhpCsFixer\Diff\Differ; /** - * @covers PhpCsFixer\Diff\v3_0\Output\DiffOnlyOutputBuilder + * @covers PhpCsFixer\Diff\Output\DiffOnlyOutputBuilder * - * @uses PhpCsFixer\Diff\v3_0\Differ - * @uses PhpCsFixer\Diff\v3_0\TimeEfficientLongestCommonSubsequenceCalculator + * @uses PhpCsFixer\Diff\Differ + * @uses PhpCsFixer\Diff\TimeEfficientLongestCommonSubsequenceCalculator */ final class DiffOnlyOutputBuilderTest extends TestCase { diff --git a/tests/v3_0/Output/Integration/StrictUnifiedDiffOutputBuilderIntegrationTest.php b/tests/Output/Integration/StrictUnifiedDiffOutputBuilderIntegrationTest.php similarity index 95% rename from tests/v3_0/Output/Integration/StrictUnifiedDiffOutputBuilderIntegrationTest.php rename to tests/Output/Integration/StrictUnifiedDiffOutputBuilderIntegrationTest.php index 93e105f7..1c28f941 100644 --- a/tests/v3_0/Output/Integration/StrictUnifiedDiffOutputBuilderIntegrationTest.php +++ b/tests/Output/Integration/StrictUnifiedDiffOutputBuilderIntegrationTest.php @@ -8,19 +8,19 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0\Output; +namespace PhpCsFixer\Diff\Output; use PHPUnit\Framework\TestCase; -use PhpCsFixer\Diff\v3_0\Differ; -use PhpCsFixer\Diff\v3_0\Utils\FileUtils; -use PhpCsFixer\Diff\v3_0\Utils\UnifiedDiffAssertTrait; +use PhpCsFixer\Diff\Differ; +use PhpCsFixer\Diff\Utils\FileUtils; +use PhpCsFixer\Diff\Utils\UnifiedDiffAssertTrait; use Symfony\Component\Process\Process; /** - * @covers PhpCsFixer\Diff\v3_0\Output\StrictUnifiedDiffOutputBuilder + * @covers PhpCsFixer\Diff\Output\StrictUnifiedDiffOutputBuilder * - * @uses PhpCsFixer\Diff\v3_0\Differ - * @uses PhpCsFixer\Diff\v3_0\TimeEfficientLongestCommonSubsequenceCalculator + * @uses PhpCsFixer\Diff\Differ + * @uses PhpCsFixer\Diff\TimeEfficientLongestCommonSubsequenceCalculator * * @requires OS Linux */ @@ -162,7 +162,7 @@ public function provideFilePairs() { $cases = []; $fromFile = __FILE__; - $vendorDir = \realpath(__DIR__ . '/../../../../vendor'); + $vendorDir = \realpath(__DIR__ . '/../../../vendor'); $fileIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($vendorDir, \RecursiveDirectoryIterator::SKIP_DOTS)); diff --git a/tests/v3_0/Output/Integration/UnifiedDiffOutputBuilderIntegrationTest.php b/tests/Output/Integration/UnifiedDiffOutputBuilderIntegrationTest.php similarity index 94% rename from tests/v3_0/Output/Integration/UnifiedDiffOutputBuilderIntegrationTest.php rename to tests/Output/Integration/UnifiedDiffOutputBuilderIntegrationTest.php index c6b51ab0..f9f5a2fe 100644 --- a/tests/v3_0/Output/Integration/UnifiedDiffOutputBuilderIntegrationTest.php +++ b/tests/Output/Integration/UnifiedDiffOutputBuilderIntegrationTest.php @@ -8,17 +8,17 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0\Output; +namespace PhpCsFixer\Diff\Output; use PHPUnit\Framework\TestCase; -use PhpCsFixer\Diff\v3_0\Utils\UnifiedDiffAssertTrait; +use PhpCsFixer\Diff\Utils\UnifiedDiffAssertTrait; use Symfony\Component\Process\Process; /** - * @covers PhpCsFixer\Diff\v3_0\Output\UnifiedDiffOutputBuilder + * @covers PhpCsFixer\Diff\Output\UnifiedDiffOutputBuilder * - * @uses PhpCsFixer\Diff\v3_0\Differ - * @uses PhpCsFixer\Diff\v3_0\TimeEfficientLongestCommonSubsequenceCalculator + * @uses PhpCsFixer\Diff\Differ + * @uses PhpCsFixer\Diff\TimeEfficientLongestCommonSubsequenceCalculator * * @requires OS Linux */ diff --git a/tests/v3_0/Output/StrictUnifiedDiffOutputBuilderDataProvider.php b/tests/Output/StrictUnifiedDiffOutputBuilderDataProvider.php similarity index 98% rename from tests/v3_0/Output/StrictUnifiedDiffOutputBuilderDataProvider.php rename to tests/Output/StrictUnifiedDiffOutputBuilderDataProvider.php index 984d7401..e2431a5c 100644 --- a/tests/v3_0/Output/StrictUnifiedDiffOutputBuilderDataProvider.php +++ b/tests/Output/StrictUnifiedDiffOutputBuilderDataProvider.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0\Output; +namespace PhpCsFixer\Diff\Output; final class StrictUnifiedDiffOutputBuilderDataProvider { diff --git a/tests/v3_0/Output/StrictUnifiedDiffOutputBuilderTest.php b/tests/Output/StrictUnifiedDiffOutputBuilderTest.php similarity index 98% rename from tests/v3_0/Output/StrictUnifiedDiffOutputBuilderTest.php rename to tests/Output/StrictUnifiedDiffOutputBuilderTest.php index 2131cd41..0123d9a4 100644 --- a/tests/v3_0/Output/StrictUnifiedDiffOutputBuilderTest.php +++ b/tests/Output/StrictUnifiedDiffOutputBuilderTest.php @@ -8,17 +8,17 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0\Output; +namespace PhpCsFixer\Diff\Output; use PHPUnit\Framework\TestCase; -use PhpCsFixer\Diff\v3_0\ConfigurationException; -use PhpCsFixer\Diff\v3_0\Differ; -use PhpCsFixer\Diff\v3_0\Utils\UnifiedDiffAssertTrait; +use PhpCsFixer\Diff\ConfigurationException; +use PhpCsFixer\Diff\Differ; +use PhpCsFixer\Diff\Utils\UnifiedDiffAssertTrait; /** - * @covers PhpCsFixer\Diff\v3_0\Output\StrictUnifiedDiffOutputBuilder + * @covers PhpCsFixer\Diff\Output\StrictUnifiedDiffOutputBuilder * - * @uses PhpCsFixer\Diff\v3_0\Differ + * @uses PhpCsFixer\Diff\Differ */ final class StrictUnifiedDiffOutputBuilderTest extends TestCase { diff --git a/tests/v3_0/Output/UnifiedDiffOutputBuilderDataProvider.php b/tests/Output/UnifiedDiffOutputBuilderDataProvider.php similarity index 99% rename from tests/v3_0/Output/UnifiedDiffOutputBuilderDataProvider.php rename to tests/Output/UnifiedDiffOutputBuilderDataProvider.php index da5dc271..8feb2659 100644 --- a/tests/v3_0/Output/UnifiedDiffOutputBuilderDataProvider.php +++ b/tests/Output/UnifiedDiffOutputBuilderDataProvider.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0\Output; +namespace PhpCsFixer\Diff\Output; final class UnifiedDiffOutputBuilderDataProvider { diff --git a/tests/v3_0/Output/UnifiedDiffOutputBuilderTest.php b/tests/Output/UnifiedDiffOutputBuilderTest.php similarity index 86% rename from tests/v3_0/Output/UnifiedDiffOutputBuilderTest.php rename to tests/Output/UnifiedDiffOutputBuilderTest.php index b68e350a..e3001f53 100644 --- a/tests/v3_0/Output/UnifiedDiffOutputBuilderTest.php +++ b/tests/Output/UnifiedDiffOutputBuilderTest.php @@ -8,17 +8,17 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0\Output; +namespace PhpCsFixer\Diff\Output; use PHPUnit\Framework\TestCase; -use PhpCsFixer\Diff\v3_0\Differ; +use PhpCsFixer\Diff\Differ; /** - * @covers PhpCsFixer\Diff\v3_0\Output\UnifiedDiffOutputBuilder + * @covers PhpCsFixer\Diff\Output\UnifiedDiffOutputBuilder * - * @uses PhpCsFixer\Diff\v3_0\Differ - * @uses PhpCsFixer\Diff\v3_0\Output\AbstractChunkOutputBuilder - * @uses PhpCsFixer\Diff\v3_0\TimeEfficientLongestCommonSubsequenceCalculator + * @uses PhpCsFixer\Diff\Differ + * @uses PhpCsFixer\Diff\Output\AbstractChunkOutputBuilder + * @uses PhpCsFixer\Diff\TimeEfficientLongestCommonSubsequenceCalculator */ final class UnifiedDiffOutputBuilderTest extends TestCase { diff --git a/tests/v3_0/ParserTest.php b/tests/ParserTest.php similarity index 94% rename from tests/v3_0/ParserTest.php rename to tests/ParserTest.php index 53f3a3ac..207e23d3 100644 --- a/tests/v3_0/ParserTest.php +++ b/tests/ParserTest.php @@ -8,17 +8,17 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0; +namespace PhpCsFixer\Diff; use PHPUnit\Framework\TestCase; -use PhpCsFixer\Diff\v3_0\Utils\FileUtils; +use PhpCsFixer\Diff\Utils\FileUtils; /** - * @covers PhpCsFixer\Diff\v3_0\Parser + * @covers PhpCsFixer\Diff\Parser * - * @uses PhpCsFixer\Diff\v3_0\Chunk - * @uses PhpCsFixer\Diff\v3_0\Diff - * @uses PhpCsFixer\Diff\v3_0\Line + * @uses PhpCsFixer\Diff\Chunk + * @uses PhpCsFixer\Diff\Diff + * @uses PhpCsFixer\Diff\Line */ final class ParserTest extends TestCase { diff --git a/tests/v3_0/TimeEfficientImplementationTest.php b/tests/TimeEfficientImplementationTest.php similarity index 80% rename from tests/v3_0/TimeEfficientImplementationTest.php rename to tests/TimeEfficientImplementationTest.php index a68c6cc1..66523122 100644 --- a/tests/v3_0/TimeEfficientImplementationTest.php +++ b/tests/TimeEfficientImplementationTest.php @@ -8,10 +8,10 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0; +namespace PhpCsFixer\Diff; /** - * @covers PhpCsFixer\Diff\v3_0\TimeEfficientLongestCommonSubsequenceCalculator + * @covers PhpCsFixer\Diff\TimeEfficientLongestCommonSubsequenceCalculator */ final class TimeEfficientImplementationTest extends LongestCommonSubsequenceTest { diff --git a/tests/v3_0/Utils/FileUtils.php b/tests/Utils/FileUtils.php similarity index 94% rename from tests/v3_0/Utils/FileUtils.php rename to tests/Utils/FileUtils.php index 4ce4edd1..63c6c969 100644 --- a/tests/v3_0/Utils/FileUtils.php +++ b/tests/Utils/FileUtils.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0\Utils; +namespace PhpCsFixer\Diff\Utils; final class FileUtils { diff --git a/tests/v3_0/Utils/UnifiedDiffAssertTrait.php b/tests/Utils/UnifiedDiffAssertTrait.php similarity index 99% rename from tests/v3_0/Utils/UnifiedDiffAssertTrait.php rename to tests/Utils/UnifiedDiffAssertTrait.php index cc3ca641..56093e88 100644 --- a/tests/v3_0/Utils/UnifiedDiffAssertTrait.php +++ b/tests/Utils/UnifiedDiffAssertTrait.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0\Utils; +namespace PhpCsFixer\Diff\Utils; trait UnifiedDiffAssertTrait { diff --git a/tests/v3_0/Utils/UnifiedDiffAssertTraitIntegrationTest.php b/tests/Utils/UnifiedDiffAssertTraitIntegrationTest.php similarity index 97% rename from tests/v3_0/Utils/UnifiedDiffAssertTraitIntegrationTest.php rename to tests/Utils/UnifiedDiffAssertTraitIntegrationTest.php index d49a206b..b61091be 100644 --- a/tests/v3_0/Utils/UnifiedDiffAssertTraitIntegrationTest.php +++ b/tests/Utils/UnifiedDiffAssertTraitIntegrationTest.php @@ -8,7 +8,7 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0\Utils; +namespace PhpCsFixer\Diff\Utils; use PHPUnit\Framework\TestCase; use Symfony\Component\Process\Process; @@ -102,7 +102,7 @@ public function provideFilePairsCases() } // create cases based on PHP files within the vendor directory for integration testing - $dir = \realpath(__DIR__ . '/../../../vendor'); + $dir = \realpath(__DIR__ . '/../../vendor'); $dirLength = \strlen($dir); $fileIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS)); diff --git a/tests/v3_0/Utils/UnifiedDiffAssertTraitTest.php b/tests/Utils/UnifiedDiffAssertTraitTest.php similarity index 99% rename from tests/v3_0/Utils/UnifiedDiffAssertTraitTest.php rename to tests/Utils/UnifiedDiffAssertTraitTest.php index 882e1f26..2172838b 100644 --- a/tests/v3_0/Utils/UnifiedDiffAssertTraitTest.php +++ b/tests/Utils/UnifiedDiffAssertTraitTest.php @@ -8,12 +8,12 @@ * file that was distributed with this source code. */ -namespace PhpCsFixer\Diff\v3_0\Utils; +namespace PhpCsFixer\Diff\Utils; use PHPUnit\Framework\TestCase; /** - * @covers PhpCsFixer\Diff\v3_0\Utils\UnifiedDiffAssertTrait + * @covers PhpCsFixer\Diff\Utils\UnifiedDiffAssertTrait */ final class UnifiedDiffAssertTraitTest extends TestCase { diff --git a/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/fixtures/.editorconfig b/tests/fixtures/.editorconfig similarity index 100% rename from tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/fixtures/.editorconfig rename to tests/fixtures/.editorconfig diff --git a/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_a.txt b/tests/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_a.txt similarity index 100% rename from tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_a.txt rename to tests/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_a.txt diff --git a/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_b.txt b/tests/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_b.txt similarity index 100% rename from tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_b.txt rename to tests/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_b.txt diff --git a/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_a.txt b/tests/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_a.txt similarity index 100% rename from tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_a.txt rename to tests/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_a.txt diff --git a/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_b.txt b/tests/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_b.txt similarity index 100% rename from tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_b.txt rename to tests/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_b.txt diff --git a/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/out/.editorconfig b/tests/fixtures/out/.editorconfig similarity index 100% rename from tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/out/.editorconfig rename to tests/fixtures/out/.editorconfig diff --git a/tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/out/.gitignore b/tests/fixtures/out/.gitignore similarity index 100% rename from tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/out/.gitignore rename to tests/fixtures/out/.gitignore diff --git a/tests/v1_4/fixtures/patch.txt b/tests/fixtures/patch.txt similarity index 100% rename from tests/v1_4/fixtures/patch.txt rename to tests/fixtures/patch.txt diff --git a/tests/v1_4/fixtures/patch2.txt b/tests/fixtures/patch2.txt similarity index 100% rename from tests/v1_4/fixtures/patch2.txt rename to tests/fixtures/patch2.txt diff --git a/tests/v1_4/ChunkTest.php b/tests/v1_4/ChunkTest.php deleted file mode 100644 index b7fe72f0..00000000 --- a/tests/v1_4/ChunkTest.php +++ /dev/null @@ -1,68 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v1_4; - -use PHPUnit\Framework\TestCase; - -/** - * @covers \PhpCsFixer\Diff\v1_4\Chunk - */ -class ChunkTest extends TestCase -{ - /** - * @var Chunk - */ - private $chunk; - - protected function setUp() - { - $this->chunk = new Chunk; - } - - public function testCanBeCreatedWithoutArguments() - { - $this->assertInstanceOf('PhpCsFixer\Diff\v1_4\Chunk', $this->chunk); - } - - public function testStartCanBeRetrieved() - { - $this->assertEquals(0, $this->chunk->getStart()); - } - - public function testStartRangeCanBeRetrieved() - { - $this->assertEquals(1, $this->chunk->getStartRange()); - } - - public function testEndCanBeRetrieved() - { - $this->assertEquals(0, $this->chunk->getEnd()); - } - - public function testEndRangeCanBeRetrieved() - { - $this->assertEquals(1, $this->chunk->getEndRange()); - } - - public function testLinesCanBeRetrieved() - { - $this->assertEquals(array(), $this->chunk->getLines()); - } - - public function testLinesCanBeSet() - { - $this->assertEquals(array(), $this->chunk->getLines()); - - $testValue = array('line0', 'line1'); - $this->chunk->setLines($testValue); - $this->assertEquals($testValue, $this->chunk->getLines()); - } -} diff --git a/tests/v1_4/DiffTest.php b/tests/v1_4/DiffTest.php deleted file mode 100644 index 7be70579..00000000 --- a/tests/v1_4/DiffTest.php +++ /dev/null @@ -1,55 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v1_4; - -use PHPUnit\Framework\TestCase; - -/** - * @covers \PhpCsFixer\Diff\v1_4\Diff - * - * @uses \PhpCsFixer\Diff\v1_4\Chunk - */ -final class DiffTest extends TestCase -{ - public function testGettersAfterConstructionWithDefault() - { - $from = 'line1a'; - $to = 'line2a'; - $diff = new Diff($from, $to); - - $this->assertSame($from, $diff->getFrom()); - $this->assertSame($to, $diff->getTo()); - $this->assertSame(array(), $diff->getChunks(), 'Expect chunks to be default value "array()".'); - } - - public function testGettersAfterConstructionWithChunks() - { - $from = 'line1b'; - $to = 'line2b'; - $chunks = array(new Chunk(), new Chunk(2, 3)); - - $diff = new Diff($from, $to, $chunks); - - $this->assertSame($from, $diff->getFrom()); - $this->assertSame($to, $diff->getTo()); - $this->assertSame($chunks, $diff->getChunks(), 'Expect chunks to be passed value.'); - } - - public function testSetChunksAfterConstruction() - { - $diff = new Diff('line1c', 'line2c'); - $this->assertSame(array(), $diff->getChunks(), 'Expect chunks to be default value "array()".'); - - $chunks = array(new Chunk(), new Chunk(2, 3)); - $diff->setChunks($chunks); - $this->assertSame($chunks, $diff->getChunks(), 'Expect chunks to be passed value.'); - } -} diff --git a/tests/v1_4/DifferTest.php b/tests/v1_4/DifferTest.php deleted file mode 100644 index 3c382a77..00000000 --- a/tests/v1_4/DifferTest.php +++ /dev/null @@ -1,388 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v1_4; - -use PhpCsFixer\Diff\v1_4\LCS\MemoryEfficientImplementation; -use PhpCsFixer\Diff\v1_4\LCS\TimeEfficientImplementation; -use PHPUnit\Framework\TestCase; - -/** - * @covers \PhpCsFixer\Diff\v1_4\Differ - * - * @uses \PhpCsFixer\Diff\v1_4\LCS\MemoryEfficientImplementation - * @uses \PhpCsFixer\Diff\v1_4\LCS\TimeEfficientImplementation - * @uses \PhpCsFixer\Diff\v1_4\Chunk - * @uses \PhpCsFixer\Diff\v1_4\Diff - * @uses \PhpCsFixer\Diff\v1_4\Line - * @uses \PhpCsFixer\Diff\v1_4\Parser - */ -class DifferTest extends TestCase -{ - const REMOVED = 2; - const ADDED = 1; - const OLD = 0; - - /** - * @var Differ - */ - private $differ; - - protected function setUp() - { - $this->differ = new Differ; - } - - /** - * @param array $expected - * @param string|array $from - * @param string|array $to - * @dataProvider arrayProvider - */ - public function testArrayRepresentationOfDiffCanBeRenderedUsingTimeEfficientLcsImplementation(array $expected, $from, $to) - { - $this->assertEquals($expected, $this->differ->diffToArray($from, $to, new TimeEfficientImplementation)); - } - - /** - * @param string $expected - * @param string $from - * @param string $to - * @dataProvider textProvider - */ - public function testTextRepresentationOfDiffCanBeRenderedUsingTimeEfficientLcsImplementation($expected, $from, $to) - { - $this->assertEquals($expected, $this->differ->diff($from, $to, new TimeEfficientImplementation)); - } - - /** - * @param array $expected - * @param string|array $from - * @param string|array $to - * @dataProvider arrayProvider - */ - public function testArrayRepresentationOfDiffCanBeRenderedUsingMemoryEfficientLcsImplementation(array $expected, $from, $to) - { - $this->assertEquals($expected, $this->differ->diffToArray($from, $to, new MemoryEfficientImplementation)); - } - - /** - * @param string $expected - * @param string $from - * @param string $to - * @dataProvider textProvider - */ - public function testTextRepresentationOfDiffCanBeRenderedUsingMemoryEfficientLcsImplementation($expected, $from, $to) - { - $this->assertEquals($expected, $this->differ->diff($from, $to, new MemoryEfficientImplementation)); - } - - public function testCustomHeaderCanBeUsed() - { - $differ = new Differ('CUSTOM HEADER'); - - $this->assertEquals( - "CUSTOM HEADER@@ @@\n-a\n+b\n", - $differ->diff('a', 'b') - ); - } - - public function testTypesOtherThanArrayAndStringCanBePassed() - { - $this->assertEquals( - "--- Original\n+++ New\n@@ @@\n-1\n+2\n", - $this->differ->diff(1, 2) - ); - } - - public function arrayProvider() - { - return array( - array( - array( - array('a', self::REMOVED), - array('b', self::ADDED) - ), - 'a', - 'b' - ), - array( - array( - array('ba', self::REMOVED), - array('bc', self::ADDED) - ), - 'ba', - 'bc' - ), - array( - array( - array('ab', self::REMOVED), - array('cb', self::ADDED) - ), - 'ab', - 'cb' - ), - array( - array( - array('abc', self::REMOVED), - array('adc', self::ADDED) - ), - 'abc', - 'adc' - ), - array( - array( - array('ab', self::REMOVED), - array('abc', self::ADDED) - ), - 'ab', - 'abc' - ), - array( - array( - array('bc', self::REMOVED), - array('abc', self::ADDED) - ), - 'bc', - 'abc' - ), - array( - array( - array('abc', self::REMOVED), - array('abbc', self::ADDED) - ), - 'abc', - 'abbc' - ), - array( - array( - array('abcdde', self::REMOVED), - array('abcde', self::ADDED) - ), - 'abcdde', - 'abcde' - ), - 'same start' => array( - array( - array(17, self::OLD), - array('b', self::REMOVED), - array('d', self::ADDED), - ), - array(30 => 17, 'a' => 'b'), - array(30 => 17, 'c' => 'd'), - ), - 'same end' => array( - array( - array(1, self::REMOVED), - array(2, self::ADDED), - array('b', self::OLD), - ), - array(1 => 1, 'a' => 'b'), - array(1 => 2, 'a' => 'b'), - ), - 'same start (2), same end (1)' => array( - array( - array(17, self::OLD), - array(2, self::OLD), - array(4, self::REMOVED), - array('a', self::ADDED), - array(5, self::ADDED), - array('x', self::OLD), - ), - array(30 => 17, 1 => 2, 2 => 4, 'z' => 'x'), - array(30 => 17, 1 => 2, 3 => 'a', 2 => 5, 'z' => 'x'), - ), - 'same' => array( - array( - array('x', self::OLD), - ), - array('z' => 'x'), - array('z' => 'x'), - ), - 'diff' => array( - array( - array('y', self::REMOVED), - array('x', self::ADDED), - ), - array('x' => 'y'), - array('z' => 'x'), - ), - 'diff 2' => array( - array( - array('y', self::REMOVED), - array('b', self::REMOVED), - array('x', self::ADDED), - array('d', self::ADDED), - ), - array('x' => 'y', 'a' => 'b'), - array('z' => 'x', 'c' => 'd'), - ), - 'test line diff detection' => array( - array( - array( - '#Warnings contain different line endings!', - self::OLD, - ), - array( - 'assertSame($expected, $differ->diff($from, $to)); - } - - public function textForNoNonDiffLinesProvider() - { - return array( - array( - '', 'a', 'a' - ), - array( - "-A\n+C\n", - "A\n\n\nB", - "C\n\n\nB", - ), - ); - } - - /** - * @requires PHPUnit 5.7 - */ - public function testDiffToArrayInvalidFromType() - { - $differ = new Differ; - - $this->expectException('\InvalidArgumentException'); - $this->expectExceptionMessageRegExp('#^"from" must be an array or string\.$#'); - - $differ->diffToArray(null, ''); - } - - /** - * @requires PHPUnit 5.7 - */ - public function testDiffInvalidToType() - { - $differ = new Differ; - - $this->expectException('\InvalidArgumentException'); - $this->expectExceptionMessageRegExp('#^"to" must be an array or string\.$#'); - - $differ->diffToArray('', new \stdClass); - } -} diff --git a/tests/v1_4/LCS/LongestCommonSubsequenceTest.php b/tests/v1_4/LCS/LongestCommonSubsequenceTest.php deleted file mode 100644 index d43cb036..00000000 --- a/tests/v1_4/LCS/LongestCommonSubsequenceTest.php +++ /dev/null @@ -1,198 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v1_4\LCS; - -use PHPUnit\Framework\TestCase; - -abstract class LongestCommonSubsequenceTest extends TestCase -{ - /** - * @var LongestCommonSubsequence - */ - private $implementation; - - /** - * @var string - */ - private $memoryLimit; - - /** - * @var int[] - */ - private $stress_sizes = array(1, 2, 3, 100, 500, 1000, 2000); - - protected function setUp() - { - $this->memoryLimit = \ini_get('memory_limit'); - \ini_set('memory_limit', '256M'); - - $this->implementation = $this->createImplementation(); - } - - /** - * @return LongestCommonSubsequence - */ - abstract protected function createImplementation(); - - protected function tearDown() - { - \ini_set('memory_limit', $this->memoryLimit); - } - - public function testBothEmpty() - { - $from = array(); - $to = array(); - $common = $this->implementation->calculate($from, $to); - - $this->assertEquals(array(), $common); - } - - public function testIsStrictComparison() - { - $from = array( - false, 0, 0.0, '', null, array(), - true, 1, 1.0, 'foo', array('foo', 'bar'), array('foo' => 'bar') - ); - $to = $from; - $common = $this->implementation->calculate($from, $to); - - $this->assertEquals($from, $common); - - $to = array( - false, false, false, false, false, false, - true, true, true, true, true, true - ); - - $expected = array( - false, - true, - ); - - $common = $this->implementation->calculate($from, $to); - - $this->assertEquals($expected, $common); - } - - public function testEqualSequences() - { - foreach ($this->stress_sizes as $size) { - $range = \range(1, $size); - $from = $range; - $to = $range; - $common = $this->implementation->calculate($from, $to); - - $this->assertEquals($range, $common); - } - } - - public function testDistinctSequences() - { - $from = array('A'); - $to = array('B'); - $common = $this->implementation->calculate($from, $to); - $this->assertEquals(array(), $common); - - $from = array('A', 'B', 'C'); - $to = array('D', 'E', 'F'); - $common = $this->implementation->calculate($from, $to); - $this->assertEquals(array(), $common); - - foreach ($this->stress_sizes as $size) { - $from = \range(1, $size); - $to = \range($size + 1, $size * 2); - $common = $this->implementation->calculate($from, $to); - $this->assertEquals(array(), $common); - } - } - - public function testCommonSubsequence() - { - $from = array('A', 'C', 'E', 'F', 'G'); - $to = array('A', 'B', 'D', 'E', 'H'); - $expected = array('A', 'E'); - $common = $this->implementation->calculate($from, $to); - $this->assertEquals($expected, $common); - - $from = array('A', 'C', 'E', 'F', 'G'); - $to = array('B', 'C', 'D', 'E', 'F', 'H'); - $expected = array('C', 'E', 'F'); - $common = $this->implementation->calculate($from, $to); - $this->assertEquals($expected, $common); - - foreach ($this->stress_sizes as $size) { - $from = $size < 2 ? array(1) : \range(1, $size + 1, 2); - $to = $size < 3 ? array(1) : \range(1, $size + 1, 3); - $expected = $size < 6 ? array(1) : \range(1, $size + 1, 6); - $common = $this->implementation->calculate($from, $to); - - $this->assertEquals($expected, $common); - } - } - - public function testSingleElementSubsequenceAtStart() - { - foreach ($this->stress_sizes as $size) { - $from = \range(1, $size); - $to = \array_slice($from, 0, 1); - $common = $this->implementation->calculate($from, $to); - - $this->assertEquals($to, $common); - } - } - - public function testSingleElementSubsequenceAtMiddle() - { - foreach ($this->stress_sizes as $size) { - $from = \range(1, $size); - $to = \array_slice($from, (int) $size / 2, 1); - $common = $this->implementation->calculate($from, $to); - - $this->assertEquals($to, $common); - } - } - - public function testSingleElementSubsequenceAtEnd() - { - foreach ($this->stress_sizes as $size) { - $from = \range(1, $size); - $to = \array_slice($from, $size - 1, 1); - $common = $this->implementation->calculate($from, $to); - - $this->assertEquals($to, $common); - } - } - - public function testReversedSequences() - { - $from = array('A', 'B'); - $to = array('B', 'A'); - $expected = array('A'); - $common = $this->implementation->calculate($from, $to); - $this->assertEquals($expected, $common); - - foreach ($this->stress_sizes as $size) { - $from = \range(1, $size); - $to = \array_reverse($from); - $common = $this->implementation->calculate($from, $to); - - $this->assertEquals(array(1), $common); - } - } - - public function testStrictTypeCalculate() - { - $diff = $this->implementation->calculate(array('5'), array('05')); - - $this->assertInternalType('array', $diff); - $this->assertCount(0, $diff); - } -} diff --git a/tests/v1_4/LCS/MemoryEfficientImplementationTest.php b/tests/v1_4/LCS/MemoryEfficientImplementationTest.php deleted file mode 100644 index 98b0b266..00000000 --- a/tests/v1_4/LCS/MemoryEfficientImplementationTest.php +++ /dev/null @@ -1,22 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v1_4\LCS; - -/** - * @covers \PhpCsFixer\Diff\v1_4\LCS\MemoryEfficientImplementation - */ -class MemoryEfficientImplementationTest extends LongestCommonSubsequenceTest -{ - protected function createImplementation() - { - return new MemoryEfficientImplementation; - } -} diff --git a/tests/v1_4/LCS/TimeEfficientImplementationTest.php b/tests/v1_4/LCS/TimeEfficientImplementationTest.php deleted file mode 100644 index e78e04a8..00000000 --- a/tests/v1_4/LCS/TimeEfficientImplementationTest.php +++ /dev/null @@ -1,22 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v1_4\LCS; - -/** - * @covers \PhpCsFixer\Diff\v1_4\LCS\TimeEfficientImplementation - */ -class TimeEfficientImplementationTest extends LongestCommonSubsequenceTest -{ - protected function createImplementation() - { - return new TimeEfficientImplementation; - } -} diff --git a/tests/v1_4/LineTest.php b/tests/v1_4/LineTest.php deleted file mode 100644 index fd73f7e2..00000000 --- a/tests/v1_4/LineTest.php +++ /dev/null @@ -1,44 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v1_4; - -use PHPUnit\Framework\TestCase; - -/** - * @covers \PhpCsFixer\Diff\v1_4\Line - */ -class LineTest extends TestCase -{ - /** - * @var Line - */ - private $line; - - protected function setUp() - { - $this->line = new Line; - } - - public function testCanBeCreatedWithoutArguments() - { - $this->assertInstanceOf('PhpCsFixer\Diff\v1_4\Line', $this->line); - } - - public function testTypeCanBeRetrieved() - { - $this->assertEquals(Line::UNCHANGED, $this->line->getType()); - } - - public function testContentCanBeRetrieved() - { - $this->assertEquals('', $this->line->getContent()); - } -} diff --git a/tests/v1_4/ParserTest.php b/tests/v1_4/ParserTest.php deleted file mode 100644 index 01482b78..00000000 --- a/tests/v1_4/ParserTest.php +++ /dev/null @@ -1,151 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v1_4; - -use PHPUnit\Framework\TestCase; - -/** - * @covers \PhpCsFixer\Diff\v1_4\Parser - * - * @uses \PhpCsFixer\Diff\v1_4\Chunk - * @uses \PhpCsFixer\Diff\v1_4\Diff - * @uses \PhpCsFixer\Diff\v1_4\Line - */ -class ParserTest extends TestCase -{ - /** - * @var Parser - */ - private $parser; - - protected function setUp() - { - $this->parser = new Parser; - } - - public function testParse() - { - $content = \file_get_contents(__DIR__ . '/fixtures/patch.txt'); - - $diffs = $this->parser->parse($content); - - $this->assertInternalType('array', $diffs); - $this->assertContainsOnlyInstancesOf('PhpCsFixer\Diff\v1_4\Diff', $diffs); - $this->assertCount(1, $diffs); - - $chunks = $diffs[0]->getChunks(); - $this->assertInternalType('array', $chunks); - $this->assertContainsOnlyInstancesOf('PhpCsFixer\Diff\v1_4\Chunk', $chunks); - - $this->assertCount(1, $chunks); - - $this->assertEquals(20, $chunks[0]->getStart()); - - $this->assertCount(4, $chunks[0]->getLines()); - } - - public function testParseWithMultipleChunks() - { - $content = \file_get_contents(__DIR__ . '/fixtures/patch2.txt'); - - $diffs = $this->parser->parse($content); - - $this->assertCount(1, $diffs); - - $chunks = $diffs[0]->getChunks(); - $this->assertCount(3, $chunks); - - $this->assertEquals(20, $chunks[0]->getStart()); - $this->assertEquals(320, $chunks[1]->getStart()); - $this->assertEquals(600, $chunks[2]->getStart()); - - $this->assertCount(5, $chunks[0]->getLines()); - $this->assertCount(5, $chunks[1]->getLines()); - $this->assertCount(4, $chunks[2]->getLines()); - } - - public function testParseWithRemovedLines() - { - $content = <<parser->parse($content); - $this->assertInternalType('array', $diffs); - $this->assertContainsOnlyInstancesOf('PhpCsFixer\Diff\v1_4\Diff', $diffs); - $this->assertCount(1, $diffs); - - $chunks = $diffs[0]->getChunks(); - - $this->assertInternalType('array', $chunks); - $this->assertContainsOnlyInstancesOf('PhpCsFixer\Diff\v1_4\Chunk', $chunks); - $this->assertCount(1, $chunks); - - $chunk = $chunks[0]; - $this->assertSame(49, $chunk->getStart()); - $this->assertSame(49, $chunk->getEnd()); - $this->assertSame(9, $chunk->getStartRange()); - $this->assertSame(8, $chunk->getEndRange()); - - $lines = $chunk->getLines(); - $this->assertInternalType('array', $lines); - $this->assertContainsOnlyInstancesOf('PhpCsFixer\Diff\v1_4\Line', $lines); - $this->assertCount(2, $lines); - - /** @var Line $line */ - $line = $lines[0]; - $this->assertSame('A', $line->getContent()); - $this->assertSame(Line::UNCHANGED, $line->getType()); - - $line = $lines[1]; - $this->assertSame('B', $line->getContent()); - $this->assertSame(Line::REMOVED, $line->getType()); - } - - public function testParseDiffForMulitpleFiles() - { - $content = <<parser->parse($content); - $this->assertCount(2, $diffs); - - /** @var Diff $diff */ - $diff = $diffs[0]; - $this->assertSame('a/Test.txt', $diff->getFrom()); - $this->assertSame('b/Test.txt', $diff->getTo()); - $this->assertCount(1, $diff->getChunks()); - - $diff = $diffs[1]; - $this->assertSame('a/Test2.txt', $diff->getFrom()); - $this->assertSame('b/Test2.txt', $diff->getTo()); - $this->assertCount(1, $diff->getChunks()); - } -} diff --git a/tests/v2_0/ChunkTest.php b/tests/v2_0/ChunkTest.php deleted file mode 100644 index 6fa49e80..00000000 --- a/tests/v2_0/ChunkTest.php +++ /dev/null @@ -1,68 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v2_0; - -use PHPUnit\Framework\TestCase; - -/** - * @covers \PhpCsFixer\Diff\v2_0\Chunk - */ -final class ChunkTest extends TestCase -{ - /** - * @var Chunk - */ - private $chunk; - - protected function setUp() - { - $this->chunk = new Chunk; - } - - public function testCanBeCreatedWithoutArguments() - { - $this->assertInstanceOf(Chunk::class, $this->chunk); - } - - public function testStartCanBeRetrieved() - { - $this->assertSame(0, $this->chunk->getStart()); - } - - public function testStartRangeCanBeRetrieved() - { - $this->assertSame(1, $this->chunk->getStartRange()); - } - - public function testEndCanBeRetrieved() - { - $this->assertSame(0, $this->chunk->getEnd()); - } - - public function testEndRangeCanBeRetrieved() - { - $this->assertSame(1, $this->chunk->getEndRange()); - } - - public function testLinesCanBeRetrieved() - { - $this->assertSame([], $this->chunk->getLines()); - } - - public function testLinesCanBeSet() - { - $this->assertSame([], $this->chunk->getLines()); - - $testValue = ['line0', 'line1']; - $this->chunk->setLines($testValue); - $this->assertSame($testValue, $this->chunk->getLines()); - } -} diff --git a/tests/v2_0/CommonChunksTestOutputBuilder.php b/tests/v2_0/CommonChunksTestOutputBuilder.php deleted file mode 100644 index 73050b5c..00000000 --- a/tests/v2_0/CommonChunksTestOutputBuilder.php +++ /dev/null @@ -1,30 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v2_0\Tests; - -use PhpCsFixer\Diff\v2_0\Output\AbstractChunkOutputBuilder; - -/** - * @author SpacePossum - * @internal - */ -final class CommonChunksTestOutputBuilder extends AbstractChunkOutputBuilder -{ - public function getDiff(array $diff) - { - return ''; - } - - public function getChunks(array $diff, $lineThreshold) - { - return $this->getCommonChunks($diff, $lineThreshold); - } -} diff --git a/tests/v2_0/DiffTest.php b/tests/v2_0/DiffTest.php deleted file mode 100644 index ac2f8ffc..00000000 --- a/tests/v2_0/DiffTest.php +++ /dev/null @@ -1,55 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v2_0; - -use PHPUnit\Framework\TestCase; - -/** - * @covers \PhpCsFixer\Diff\v2_0\Diff - * - * @uses \PhpCsFixer\Diff\v2_0\Chunk - */ -final class DiffTest extends TestCase -{ - public function testGettersAfterConstructionWithDefault() - { - $from = 'line1a'; - $to = 'line2a'; - $diff = new Diff($from, $to); - - $this->assertSame($from, $diff->getFrom()); - $this->assertSame($to, $diff->getTo()); - $this->assertSame([], $diff->getChunks(), 'Expect chunks to be default value "array()".'); - } - - public function testGettersAfterConstructionWithChunks() - { - $from = 'line1b'; - $to = 'line2b'; - $chunks = [new Chunk(), new Chunk(2, 3)]; - - $diff = new Diff($from, $to, $chunks); - - $this->assertSame($from, $diff->getFrom()); - $this->assertSame($to, $diff->getTo()); - $this->assertSame($chunks, $diff->getChunks(), 'Expect chunks to be passed value.'); - } - - public function testSetChunksAfterConstruction() - { - $diff = new Diff('line1c', 'line2c'); - $this->assertSame([], $diff->getChunks(), 'Expect chunks to be default value "array()".'); - - $chunks = [new Chunk(), new Chunk(2, 3)]; - $diff->setChunks($chunks); - $this->assertSame($chunks, $diff->getChunks(), 'Expect chunks to be passed value.'); - } -} diff --git a/tests/v2_0/DifferTest.php b/tests/v2_0/DifferTest.php deleted file mode 100644 index 0283897f..00000000 --- a/tests/v2_0/DifferTest.php +++ /dev/null @@ -1,1017 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v2_0; - -use PHPUnit\Framework\TestCase; -use PhpCsFixer\Diff\v2_0\Output\DiffOnlyOutputBuilder; -use PhpCsFixer\Diff\v2_0\Output\UnifiedDiffOutputBuilder; -use PhpCsFixer\Diff\v2_0\Tests\CommonChunksTestOutputBuilder; - -/** - * @covers \PhpCsFixer\Diff\v2_0\Differ - * @covers \PhpCsFixer\Diff\v2_0\Output\AbstractChunkOutputBuilder - * @covers \PhpCsFixer\Diff\v2_0\Output\DiffOnlyOutputBuilder - * @covers \PhpCsFixer\Diff\v2_0\Output\UnifiedDiffOutputBuilder - * - * @uses \PhpCsFixer\Diff\v2_0\MemoryEfficientLongestCommonSubsequenceCalculator - * @uses \PhpCsFixer\Diff\v2_0\TimeEfficientLongestCommonSubsequenceCalculator - * @uses \PhpCsFixer\Diff\v2_0\Chunk - * @uses \PhpCsFixer\Diff\v2_0\Diff - * @uses \PhpCsFixer\Diff\v2_0\Line - * @uses \PhpCsFixer\Diff\v2_0\Parser - */ -final class DifferTest extends TestCase -{ - const WARNING = 3; - const REMOVED = 2; - const ADDED = 1; - const OLD = 0; - - /** - * @var Differ - */ - private $differ; - - protected function setUp() - { - $this->differ = new Differ; - } - - /** - * @param array $expected - * @param string|array $from - * @param string|array $to - * @dataProvider arrayProvider - */ - public function testArrayRepresentationOfDiffCanBeRenderedUsingTimeEfficientLcsImplementation(array $expected, $from, $to) - { - $this->assertSame($expected, $this->differ->diffToArray($from, $to, new TimeEfficientLongestCommonSubsequenceCalculator)); - } - - /** - * @param string $expected - * @param string $from - * @param string $to - * @dataProvider textProvider - */ - public function testTextRepresentationOfDiffCanBeRenderedUsingTimeEfficientLcsImplementation($expected, $from, $to) - { - $this->assertSame($expected, $this->differ->diff($from, $to, new TimeEfficientLongestCommonSubsequenceCalculator)); - } - - /** - * @param array $expected - * @param string|array $from - * @param string|array $to - * @dataProvider arrayProvider - */ - public function testArrayRepresentationOfDiffCanBeRenderedUsingMemoryEfficientLcsImplementation(array $expected, $from, $to) - { - $this->assertSame($expected, $this->differ->diffToArray($from, $to, new MemoryEfficientLongestCommonSubsequenceCalculator)); - } - - /** - * @param string $expected - * @param string $from - * @param string $to - * @dataProvider textProvider - */ - public function testTextRepresentationOfDiffCanBeRenderedUsingMemoryEfficientLcsImplementation($expected, $from, $to) - { - $this->assertSame($expected, $this->differ->diff($from, $to, new MemoryEfficientLongestCommonSubsequenceCalculator)); - } - - /** - * @param string $expected - * @param string $from - * @param string $to - * @param string $header - * @dataProvider headerProvider - */ - public function testCustomHeaderCanBeUsed($expected, $from, $to, $header) - { - $differ = new Differ(new UnifiedDiffOutputBuilder($header)); - - $this->assertSame( - $expected, - $differ->diff($from, $to) - ); - } - - public function headerProvider() - { - return [ - [ - "CUSTOM HEADER\n@@ @@\n-a\n+b\n", - 'a', - 'b', - 'CUSTOM HEADER' - ], - [ - "CUSTOM HEADER\n@@ @@\n-a\n+b\n", - 'a', - 'b', - "CUSTOM HEADER\n" - ], - [ - "CUSTOM HEADER\n\n@@ @@\n-a\n+b\n", - 'a', - 'b', - "CUSTOM HEADER\n\n" - ], - [ - "@@ @@\n-a\n+b\n", - 'a', - 'b', - '' - ], - ]; - } - - public function testTypesOtherThanArrayAndStringCanBePassed() - { - $this->assertSame( - "--- Original\n+++ New\n@@ @@\n-1\n+2\n", - $this->differ->diff(1, 2) - ); - } - - public function arrayProvider() - { - return [ - [ - [ - ['a', self::REMOVED], - ['b', self::ADDED] - ], - 'a', - 'b' - ], - [ - [ - ['ba', self::REMOVED], - ['bc', self::ADDED] - ], - 'ba', - 'bc' - ], - [ - [ - ['ab', self::REMOVED], - ['cb', self::ADDED] - ], - 'ab', - 'cb' - ], - [ - [ - ['abc', self::REMOVED], - ['adc', self::ADDED] - ], - 'abc', - 'adc' - ], - [ - [ - ['ab', self::REMOVED], - ['abc', self::ADDED] - ], - 'ab', - 'abc' - ], - [ - [ - ['bc', self::REMOVED], - ['abc', self::ADDED] - ], - 'bc', - 'abc' - ], - [ - [ - ['abc', self::REMOVED], - ['abbc', self::ADDED] - ], - 'abc', - 'abbc' - ], - [ - [ - ['abcdde', self::REMOVED], - ['abcde', self::ADDED] - ], - 'abcdde', - 'abcde' - ], - 'same start' => [ - [ - [17, self::OLD], - ['b', self::REMOVED], - ['d', self::ADDED], - ], - [30 => 17, 'a' => 'b'], - [30 => 17, 'c' => 'd'], - ], - 'same end' => [ - [ - [1, self::REMOVED], - [2, self::ADDED], - ['b', self::OLD], - ], - [1 => 1, 'a' => 'b'], - [1 => 2, 'a' => 'b'], - ], - 'same start (2), same end (1)' => [ - [ - [17, self::OLD], - [2, self::OLD], - [4, self::REMOVED], - ['a', self::ADDED], - [5, self::ADDED], - ['x', self::OLD], - ], - [30 => 17, 1 => 2, 2 => 4, 'z' => 'x'], - [30 => 17, 1 => 2, 3 => 'a', 2 => 5, 'z' => 'x'], - ], - 'same' => [ - [ - ['x', self::OLD], - ], - ['z' => 'x'], - ['z' => 'x'], - ], - 'diff' => [ - [ - ['y', self::REMOVED], - ['x', self::ADDED], - ], - ['x' => 'y'], - ['z' => 'x'], - ], - 'diff 2' => [ - [ - ['y', self::REMOVED], - ['b', self::REMOVED], - ['x', self::ADDED], - ['d', self::ADDED], - ], - ['x' => 'y', 'a' => 'b'], - ['z' => 'x', 'c' => 'd'], - ], - 'test line diff detection' => [ - [ - [ - "#Warnings contain different line endings!\n", - self::WARNING, - ], - [ - " [ - [ - [ - "#Warnings contain different line endings!\n", - self::WARNING, - ], - [ - "assertSame($expected, $differ->diff($from, $to)); - } - - public function textForNoNonDiffLinesProvider() - { - return [ - [ - " #Warnings contain different line endings!\n-A\r\n+B\n", - "A\r\n", - "B\n", - ], - [ - "-A\n+B\n", - "\nA", - "\nB" - ], - [ - '', - 'a', - 'a' - ], - [ - "-A\n+C\n", - "A\n\n\nB", - "C\n\n\nB", - ], - [ - "header\n", - 'a', - 'a', - 'header' - ], - [ - "header\n", - 'a', - 'a', - "header\n" - ], - ]; - } - - /** - * @requires PHPUnit 6.0 - */ - public function testDiffToArrayInvalidFromType() - { - $this->expectException('\InvalidArgumentException'); - $this->expectExceptionMessageRegExp('#^"from" must be an array or string\.$#'); - - $this->differ->diffToArray(null, ''); - } - - /** - * @requires PHPUnit 6.0 - */ - public function testDiffInvalidToType() - { - $this->expectException('\InvalidArgumentException'); - $this->expectExceptionMessageRegExp('#^"to" must be an array or string\.$#'); - - $this->differ->diffToArray('', new \stdClass); - } - - /** - * @param array $expected - * @param string $from - * @param string $to - * @param int $lineThreshold - * @dataProvider provideGetCommonChunks - */ - public function testGetCommonChunks(array $expected, $from, $to, $lineThreshold = 5) - { - $output = new CommonChunksTestOutputBuilder(); - - $this->assertSame( - $expected, - $output->getChunks($this->differ->diffToArray($from, $to), $lineThreshold) - ); - } - - public function provideGetCommonChunks() - { - return[ - 'same (with default threshold)' => [ - [], - 'A', - 'A', - ], - 'same (threshold 0)' => [ - [0 => 0], - 'A', - 'A', - 0, - ], - 'empty' => [ - [], - '', - '', - ], - 'single line diff' => [ - [], - 'A', - 'B', - ], - 'below threshold I' => [ - [], - "A\nX\nC", - "A\nB\nC", - ], - 'below threshold II' => [ - [], - "A\n\n\n\nX\nC", - "A\n\n\n\nB\nC", - ], - 'below threshold III' => [ - [0 => 5], - "A\n\n\n\n\n\nB", - "A\n\n\n\n\n\nA", - ], - 'same start' => [ - [0 => 5], - "A\n\n\n\n\n\nX\nC", - "A\n\n\n\n\n\nB\nC", - ], - 'same start long' => [ - [0 => 13], - "\n\n\n\n\n\n\n\n\n\n\n\n\n\nA", - "\n\n\n\n\n\n\n\n\n\n\n\n\n\nB", - ], - 'same part in between' => [ - [2 => 8], - "A\n\n\n\n\n\n\nX\nY\nZ\n\n", - "B\n\n\n\n\n\n\nX\nA\nZ\n\n", - ], - 'same trailing' => [ - [2 => 14], - "A\n\n\n\n\n\n\n\n\n\n\n\n\n\n", - "B\n\n\n\n\n\n\n\n\n\n\n\n\n\n", - ], - 'same part in between, same trailing' => [ - [2 => 7, 10 => 15], - "A\n\n\n\n\n\n\nA\n\n\n\n\n\n\n", - "B\n\n\n\n\n\n\nB\n\n\n\n\n\n\n", - ], - 'below custom threshold I' => [ - [], - "A\n\nB", - "A\n\nD", - 2 - ], - 'custom threshold I' => [ - [0 => 1], - "A\n\nB", - "A\n\nD", - 1 - ], - 'custom threshold II' => [ - [], - "A\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", - "A\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", - 19 - ], - [ - [3 => 9], - "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk", - "a\np\nc\nd\ne\nf\ng\nh\ni\nw\nk", - ], - [ - [0 => 5, 8 => 13], - "A\nA\nA\nA\nA\nA\nX\nC\nC\nC\nC\nC\nC", - "A\nA\nA\nA\nA\nA\nB\nC\nC\nC\nC\nC\nC", - ], - [ - [0 => 5, 8 => 13], - "A\nA\nA\nA\nA\nA\nX\nC\nC\nC\nC\nC\nC\nX", - "A\nA\nA\nA\nA\nA\nB\nC\nC\nC\nC\nC\nC\nY", - ], - ]; - } - - /** - * @param array $expected - * @param string $input - * @dataProvider provideSplitStringByLinesCases - */ - public function testSplitStringByLines(array $expected, $input) - { - $reflection = new \ReflectionObject($this->differ); - $method = $reflection->getMethod('splitStringByLines'); - $method->setAccessible(true); - - $this->assertSame($expected, $method->invoke($this->differ, $input)); - } - - public function provideSplitStringByLinesCases() - { - return [ - [ - [], - '' - ], - [ - ['a'], - 'a' - ], - [ - ["a\n"], - "a\n" - ], - [ - ["a\r"], - "a\r" - ], - [ - ["a\r\n"], - "a\r\n" - ], - [ - ["\n"], - "\n" - ], - [ - ["\r"], - "\r" - ], - [ - ["\r\n"], - "\r\n" - ], - [ - [ - "A\n", - "B\n", - "\n", - "C\n" - ], - "A\nB\n\nC\n", - ], - [ - [ - "A\r\n", - "B\n", - "\n", - "C\r" - ], - "A\r\nB\n\nC\r", - ], - [ - [ - "\n", - "A\r\n", - "B\n", - "\n", - 'C' - ], - "\nA\r\nB\n\nC", - ], - ]; - } - - /** - * @param string $expected - * @param string $from - * @param string $to - * @dataProvider provideDiffWithLineNumbers - */ - public function testDiffWithLineNumbers($expected, $from, $to) - { - $differ = new Differ(new UnifiedDiffOutputBuilder("--- Original\n+++ New\n", true)); - $this->assertSame($expected, $differ->diff($from, $to)); - } - - public function provideDiffWithLineNumbers() - { - return [ - 'diff line 1 non_patch_compat' => [ - '--- Original -+++ New -@@ -1 +1 @@ --AA -+BA -', - 'AA', - 'BA', - ], - 'diff line +1 non_patch_compat' => [ - '--- Original -+++ New -@@ -1 +1,2 @@ --AZ -+ -+B -', - 'AZ', - "\nB", - ], - 'diff line -1 non_patch_compat' => [ - '--- Original -+++ New -@@ -1,2 +1 @@ -- --AF -+B -', - "\nAF", - 'B', - ], - 'II non_patch_compat' => [ - '--- Original -+++ New -@@ -1,2 +1,0 @@ -- -- -' - , - "\n\nA\n1", - "A\n1", - ], - 'diff last line II - no trailing linebreak non_patch_compat' => [ - '--- Original -+++ New -@@ -8 +8 @@ --E -+B -', - "A\n\n\n\n\n\n\nE", - "A\n\n\n\n\n\n\nB", - ], - [ - "--- Original\n+++ New\n@@ -1,2 +1 @@\n \n-\n", - "\n\n", - "\n", - ], - 'diff line endings non_patch_compat' => [ - "--- Original\n+++ New\n@@ -1 +1 @@\n #Warnings contain different line endings!\n- [ - '--- Original -+++ New -', - "AT\n", - "AT\n", - ], - [ - '--- Original -+++ New -@@ -1 +1 @@ --b -+a -', - "b\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", - "a\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" - ], - 'diff line @1' => [ - '--- Original -+++ New -@@ -1,2 +1,2 @@ - ' . ' --AG -+B -', - "\nAG\n", - "\nB\n", - ], - 'same multiple lines' => [ - '--- Original -+++ New -@@ -1,3 +1,3 @@ - ' . ' - ' . ' --V -+B -' - - , - "\n\nV\nC213", - "\n\nB\nC213", - ], - 'diff last line I' => [ - '--- Original -+++ New -@@ -8 +8 @@ --E -+B -', - "A\n\n\n\n\n\n\nE\n", - "A\n\n\n\n\n\n\nB\n", - ], - 'diff line middle' => [ - '--- Original -+++ New -@@ -8 +8 @@ --X -+Z -', - "A\n\n\n\n\n\n\nX\n\n\n\n\n\n\nAY", - "A\n\n\n\n\n\n\nZ\n\n\n\n\n\n\nAY", - ], - 'diff last line III' => [ - '--- Original -+++ New -@@ -15 +15 @@ --A -+B -', - "A\n\n\n\n\n\n\nA\n\n\n\n\n\n\nA\n", - "A\n\n\n\n\n\n\nA\n\n\n\n\n\n\nB\n", - ], - [ - '--- Original -+++ New -@@ -1,7 +1,7 @@ - A --B -+B1 - D - E - EE - F --G -+G1 -', - "A\nB\nD\nE\nEE\nF\nG\nH", - "A\nB1\nD\nE\nEE\nF\nG1\nH", - ], - [ - '--- Original -+++ New -@@ -1 +1,2 @@ - Z -+ -@@ -10 +11 @@ --i -+x -', - 'Z -a -b -c -d -e -f -g -h -i -j', - 'Z - -a -b -c -d -e -f -g -h -x -j' - ], - [ - '--- Original -+++ New -@@ -1,5 +1,3 @@ -- --a -+b - A --a -- -+b -', - "\na\nA\na\n\n\nA", - "b\nA\nb\n\nA" - ], - [ - <<assertAttributeInstanceOf( - UnifiedDiffOutputBuilder::class, - 'outputBuilder', - new Differ(null) - ); - } - - public function testConstructorString() - { - $this->assertAttributeInstanceOf( - UnifiedDiffOutputBuilder::class, - 'outputBuilder', - new Differ("--- Original\n+++ New\n") - ); - } - - /** - * @requires PHPUnit 6.0 - */ - public function testConstructorInvalidArgInt() - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessageRegExp('/^Expected builder to be an instance of DiffOutputBuilderInterface, or a string, got integer "1"\.$/'); - - new Differ(1); - } - - /** - * @requires PHPUnit 6.0 - */ - public function testConstructorInvalidArgObject() - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessageRegExp('/^Expected builder to be an instance of DiffOutputBuilderInterface, or a string, got instance of "SplFileInfo"\.$/'); - - new Differ(new \SplFileInfo(__FILE__)); - } -} diff --git a/tests/v2_0/DifferTestTest.php b/tests/v2_0/DifferTestTest.php deleted file mode 100644 index c5176031..00000000 --- a/tests/v2_0/DifferTestTest.php +++ /dev/null @@ -1,83 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v2_0; - -use PHPUnit\Framework\TestCase; - -/** - * @requires OS Linux - */ -final class DifferTestTest extends TestCase -{ - private $fileFrom; - private $filePatch; - - protected function setUp() - { - $dir = \realpath(__DIR__ . '/../') . '/'; - $this->fileFrom = $dir . 'from.txt'; - $this->filePatch = $dir . 'patch.txt'; - } - - /** - * @dataProvider provideDiffWithLineNumbers - */ - public function testTheTestProvideDiffWithLineNumbers($expected, $from, $to) - { - $this->runThisTest($expected, $from, $to); - } - - public function provideDiffWithLineNumbers() - { - require_once __DIR__ . '/DifferTest.php'; - $test = new DifferTest(); - $tests = $test->provideDiffWithLineNumbers(); - - $tests = \array_filter( - $tests, - function ($key) { - return !\is_string($key) || false === \strpos($key, 'non_patch_compat'); - }, - ARRAY_FILTER_USE_KEY - ); - - return $tests; - } - - private function runThisTest($expected, $from, $to) - { - $expected = \str_replace('--- Original', '--- from.txt', $expected); - $expected = \str_replace('+++ New', '+++ from.txt', $expected); - - @\unlink($this->fileFrom); - @\unlink($this->filePatch); - - $this->assertNotFalse(\file_put_contents($this->fileFrom, $from)); - $this->assertNotFalse(\file_put_contents($this->filePatch, $expected)); - - $command = \sprintf( - 'patch -u --verbose %s < %s', // --posix - \escapeshellarg($this->fileFrom), - \escapeshellarg($this->filePatch) - ); - - \exec($command, $output, $d); - - $this->assertSame(0, $d, \sprintf('%s | %s', $command, \implode("\n", $output))); - - $patched = \file_get_contents($this->fileFrom); - $this->assertSame($patched, $to); - - @\unlink($this->fileFrom . '.orig'); - @\unlink($this->fileFrom); - @\unlink($this->filePatch); - } -} diff --git a/tests/v2_0/LineTest.php b/tests/v2_0/LineTest.php deleted file mode 100644 index 47b46a3d..00000000 --- a/tests/v2_0/LineTest.php +++ /dev/null @@ -1,44 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v2_0; - -use PHPUnit\Framework\TestCase; - -/** - * @covers \PhpCsFixer\Diff\v2_0\Line - */ -final class LineTest extends TestCase -{ - /** - * @var Line - */ - private $line; - - protected function setUp() - { - $this->line = new Line; - } - - public function testCanBeCreatedWithoutArguments() - { - $this->assertInstanceOf(Line::class, $this->line); - } - - public function testTypeCanBeRetrieved() - { - $this->assertSame(Line::UNCHANGED, $this->line->getType()); - } - - public function testContentCanBeRetrieved() - { - $this->assertSame('', $this->line->getContent()); - } -} diff --git a/tests/v2_0/LongestCommonSubsequenceTest.php b/tests/v2_0/LongestCommonSubsequenceTest.php deleted file mode 100644 index ef5471cb..00000000 --- a/tests/v2_0/LongestCommonSubsequenceTest.php +++ /dev/null @@ -1,201 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v2_0; - -use PHPUnit\Framework\TestCase; - -/** - * @coversNothing - */ -abstract class LongestCommonSubsequenceTest extends TestCase -{ - /** - * @var LongestCommonSubsequenceCalculator - */ - private $implementation; - - /** - * @var string - */ - private $memoryLimit; - - /** - * @var int[] - */ - private $stress_sizes = [1, 2, 3, 100, 500, 1000, 2000]; - - protected function setUp() - { - $this->memoryLimit = \ini_get('memory_limit'); - \ini_set('memory_limit', '256M'); - - $this->implementation = $this->createImplementation(); - } - - /** - * @return LongestCommonSubsequenceCalculator - */ - abstract protected function createImplementation(); - - protected function tearDown() - { - \ini_set('memory_limit', $this->memoryLimit); - } - - public function testBothEmpty() - { - $from = []; - $to = []; - $common = $this->implementation->calculate($from, $to); - - $this->assertSame([], $common); - } - - public function testIsStrictComparison() - { - $from = [ - false, 0, 0.0, '', null, [], - true, 1, 1.0, 'foo', ['foo', 'bar'], ['foo' => 'bar'] - ]; - $to = $from; - $common = $this->implementation->calculate($from, $to); - - $this->assertSame($from, $common); - - $to = [ - false, false, false, false, false, false, - true, true, true, true, true, true - ]; - - $expected = [ - false, - true, - ]; - - $common = $this->implementation->calculate($from, $to); - - $this->assertSame($expected, $common); - } - - public function testEqualSequences() - { - foreach ($this->stress_sizes as $size) { - $range = \range(1, $size); - $from = $range; - $to = $range; - $common = $this->implementation->calculate($from, $to); - - $this->assertSame($range, $common); - } - } - - public function testDistinctSequences() - { - $from = ['A']; - $to = ['B']; - $common = $this->implementation->calculate($from, $to); - $this->assertSame([], $common); - - $from = ['A', 'B', 'C']; - $to = ['D', 'E', 'F']; - $common = $this->implementation->calculate($from, $to); - $this->assertSame([], $common); - - foreach ($this->stress_sizes as $size) { - $from = \range(1, $size); - $to = \range($size + 1, $size * 2); - $common = $this->implementation->calculate($from, $to); - $this->assertSame([], $common); - } - } - - public function testCommonSubsequence() - { - $from = ['A', 'C', 'E', 'F', 'G']; - $to = ['A', 'B', 'D', 'E', 'H']; - $expected = ['A', 'E']; - $common = $this->implementation->calculate($from, $to); - $this->assertSame($expected, $common); - - $from = ['A', 'C', 'E', 'F', 'G']; - $to = ['B', 'C', 'D', 'E', 'F', 'H']; - $expected = ['C', 'E', 'F']; - $common = $this->implementation->calculate($from, $to); - $this->assertSame($expected, $common); - - foreach ($this->stress_sizes as $size) { - $from = $size < 2 ? [1] : \range(1, $size + 1, 2); - $to = $size < 3 ? [1] : \range(1, $size + 1, 3); - $expected = $size < 6 ? [1] : \range(1, $size + 1, 6); - $common = $this->implementation->calculate($from, $to); - - $this->assertSame($expected, $common); - } - } - - public function testSingleElementSubsequenceAtStart() - { - foreach ($this->stress_sizes as $size) { - $from = \range(1, $size); - $to = \array_slice($from, 0, 1); - $common = $this->implementation->calculate($from, $to); - - $this->assertSame($to, $common); - } - } - - public function testSingleElementSubsequenceAtMiddle() - { - foreach ($this->stress_sizes as $size) { - $from = \range(1, $size); - $to = \array_slice($from, (int) ($size / 2), 1); - $common = $this->implementation->calculate($from, $to); - - $this->assertSame($to, $common); - } - } - - public function testSingleElementSubsequenceAtEnd() - { - foreach ($this->stress_sizes as $size) { - $from = \range(1, $size); - $to = \array_slice($from, $size - 1, 1); - $common = $this->implementation->calculate($from, $to); - - $this->assertSame($to, $common); - } - } - - public function testReversedSequences() - { - $from = ['A', 'B']; - $to = ['B', 'A']; - $expected = ['A']; - $common = $this->implementation->calculate($from, $to); - $this->assertSame($expected, $common); - - foreach ($this->stress_sizes as $size) { - $from = \range(1, $size); - $to = \array_reverse($from); - $common = $this->implementation->calculate($from, $to); - - $this->assertSame([1], $common); - } - } - - public function testStrictTypeCalculate() - { - $diff = $this->implementation->calculate(['5'], ['05']); - - $this->assertInternalType('array', $diff); - $this->assertCount(0, $diff); - } -} diff --git a/tests/v2_0/MemoryEfficientImplementationTest.php b/tests/v2_0/MemoryEfficientImplementationTest.php deleted file mode 100644 index 28f98622..00000000 --- a/tests/v2_0/MemoryEfficientImplementationTest.php +++ /dev/null @@ -1,22 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v2_0; - -/** - * @covers \PhpCsFixer\Diff\v2_0\MemoryEfficientLongestCommonSubsequenceCalculator - */ -final class MemoryEfficientImplementationTest extends LongestCommonSubsequenceTest -{ - protected function createImplementation() - { - return new MemoryEfficientLongestCommonSubsequenceCalculator; - } -} diff --git a/tests/v2_0/ParserTest.php b/tests/v2_0/ParserTest.php deleted file mode 100644 index ad7b345f..00000000 --- a/tests/v2_0/ParserTest.php +++ /dev/null @@ -1,151 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v2_0; - -use PHPUnit\Framework\TestCase; - -/** - * @covers \PhpCsFixer\Diff\v2_0\Parser - * - * @uses \PhpCsFixer\Diff\v2_0\Chunk - * @uses \PhpCsFixer\Diff\v2_0\Diff - * @uses \PhpCsFixer\Diff\v2_0\Line - */ -final class ParserTest extends TestCase -{ - /** - * @var Parser - */ - private $parser; - - protected function setUp() - { - $this->parser = new Parser; - } - - public function testParse() - { - $content = \file_get_contents(__DIR__ . '/fixtures/patch.txt'); - - $diffs = $this->parser->parse($content); - - $this->assertInternalType('array', $diffs); - $this->assertContainsOnlyInstancesOf(Diff::class, $diffs); - $this->assertCount(1, $diffs); - - $chunks = $diffs[0]->getChunks(); - $this->assertInternalType('array', $chunks); - $this->assertContainsOnlyInstancesOf(Chunk::class, $chunks); - - $this->assertCount(1, $chunks); - - $this->assertSame(20, $chunks[0]->getStart()); - - $this->assertCount(4, $chunks[0]->getLines()); - } - - public function testParseWithMultipleChunks() - { - $content = \file_get_contents(__DIR__ . '/fixtures/patch2.txt'); - - $diffs = $this->parser->parse($content); - - $this->assertCount(1, $diffs); - - $chunks = $diffs[0]->getChunks(); - $this->assertCount(3, $chunks); - - $this->assertSame(20, $chunks[0]->getStart()); - $this->assertSame(320, $chunks[1]->getStart()); - $this->assertSame(600, $chunks[2]->getStart()); - - $this->assertCount(5, $chunks[0]->getLines()); - $this->assertCount(5, $chunks[1]->getLines()); - $this->assertCount(4, $chunks[2]->getLines()); - } - - public function testParseWithRemovedLines() - { - $content = <<parser->parse($content); - $this->assertInternalType('array', $diffs); - $this->assertContainsOnlyInstancesOf(Diff::class, $diffs); - $this->assertCount(1, $diffs); - - $chunks = $diffs[0]->getChunks(); - - $this->assertInternalType('array', $chunks); - $this->assertContainsOnlyInstancesOf(Chunk::class, $chunks); - $this->assertCount(1, $chunks); - - $chunk = $chunks[0]; - $this->assertSame(49, $chunk->getStart()); - $this->assertSame(49, $chunk->getEnd()); - $this->assertSame(9, $chunk->getStartRange()); - $this->assertSame(8, $chunk->getEndRange()); - - $lines = $chunk->getLines(); - $this->assertInternalType('array', $lines); - $this->assertContainsOnlyInstancesOf(Line::class, $lines); - $this->assertCount(2, $lines); - - /** @var Line $line */ - $line = $lines[0]; - $this->assertSame('A', $line->getContent()); - $this->assertSame(Line::UNCHANGED, $line->getType()); - - $line = $lines[1]; - $this->assertSame('B', $line->getContent()); - $this->assertSame(Line::REMOVED, $line->getType()); - } - - public function testParseDiffForMulitpleFiles() - { - $content = <<parser->parse($content); - $this->assertCount(2, $diffs); - - /** @var Diff $diff */ - $diff = $diffs[0]; - $this->assertSame('a/Test.txt', $diff->getFrom()); - $this->assertSame('b/Test.txt', $diff->getTo()); - $this->assertCount(1, $diff->getChunks()); - - $diff = $diffs[1]; - $this->assertSame('a/Test2.txt', $diff->getFrom()); - $this->assertSame('b/Test2.txt', $diff->getTo()); - $this->assertCount(1, $diff->getChunks()); - } -} diff --git a/tests/v2_0/TimeEfficientImplementationTest.php b/tests/v2_0/TimeEfficientImplementationTest.php deleted file mode 100644 index 16f97925..00000000 --- a/tests/v2_0/TimeEfficientImplementationTest.php +++ /dev/null @@ -1,22 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PhpCsFixer\Diff\v2_0; - -/** - * @covers \PhpCsFixer\Diff\v2_0\TimeEfficientLongestCommonSubsequenceCalculator - */ -final class TimeEfficientImplementationTest extends LongestCommonSubsequenceTest -{ - protected function createImplementation() - { - return new TimeEfficientLongestCommonSubsequenceCalculator; - } -} diff --git a/tests/v2_0/fixtures/patch.txt b/tests/v2_0/fixtures/patch.txt deleted file mode 100644 index 144b61d0..00000000 --- a/tests/v2_0/fixtures/patch.txt +++ /dev/null @@ -1,9 +0,0 @@ -diff --git a/Foo.php b/Foo.php -index abcdefg..abcdefh 100644 ---- a/Foo.php -+++ b/Foo.php -@@ -20,4 +20,5 @@ class Foo - const ONE = 1; - const TWO = 2; -+ const THREE = 3; - const FOUR = 4; diff --git a/tests/v2_0/fixtures/patch2.txt b/tests/v2_0/fixtures/patch2.txt deleted file mode 100644 index 41fbc959..00000000 --- a/tests/v2_0/fixtures/patch2.txt +++ /dev/null @@ -1,21 +0,0 @@ -diff --git a/Foo.php b/Foo.php -index abcdefg..abcdefh 100644 ---- a/Foo.php -+++ b/Foo.php -@@ -20,4 +20,5 @@ class Foo - const ONE = 1; - const TWO = 2; -+ const THREE = 3; - const FOUR = 4; - -@@ -320,4 +320,5 @@ class Foo - const A = 'A'; - const B = 'B'; -+ const C = 'C'; - const D = 'D'; - -@@ -600,4 +600,5 @@ class Foo - public function doSomething() { - -+ return 'foo'; - } diff --git a/tests/v3_0/fixtures/.editorconfig b/tests/v3_0/fixtures/.editorconfig deleted file mode 100644 index 78b36ca0..00000000 --- a/tests/v3_0/fixtures/.editorconfig +++ /dev/null @@ -1 +0,0 @@ -root = true diff --git a/tests/v3_0/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_a.txt b/tests/v3_0/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_a.txt deleted file mode 100644 index 2e65efe2..00000000 --- a/tests/v3_0/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_a.txt +++ /dev/null @@ -1 +0,0 @@ -a \ No newline at end of file diff --git a/tests/v3_0/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_b.txt b/tests/v3_0/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_b.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/v3_0/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_a.txt b/tests/v3_0/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_a.txt deleted file mode 100644 index c7fe26e9..00000000 --- a/tests/v3_0/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_a.txt +++ /dev/null @@ -1,35 +0,0 @@ -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a \ No newline at end of file diff --git a/tests/v3_0/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_b.txt b/tests/v3_0/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_b.txt deleted file mode 100644 index 377a70f8..00000000 --- a/tests/v3_0/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_b.txt +++ /dev/null @@ -1,18 +0,0 @@ -a -a -a -a -a -a -a -a -a -a -b -a -a -a -a -a -a -c \ No newline at end of file diff --git a/tests/v3_0/fixtures/out/.editorconfig b/tests/v3_0/fixtures/out/.editorconfig deleted file mode 100644 index 78b36ca0..00000000 --- a/tests/v3_0/fixtures/out/.editorconfig +++ /dev/null @@ -1 +0,0 @@ -root = true diff --git a/tests/v3_0/fixtures/out/.gitignore b/tests/v3_0/fixtures/out/.gitignore deleted file mode 100644 index f6f7a478..00000000 --- a/tests/v3_0/fixtures/out/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# reset all ignore rules to create sandbox for integration test -!/** \ No newline at end of file diff --git a/tests/v3_0/fixtures/patch.txt b/tests/v3_0/fixtures/patch.txt deleted file mode 100644 index 144b61d0..00000000 --- a/tests/v3_0/fixtures/patch.txt +++ /dev/null @@ -1,9 +0,0 @@ -diff --git a/Foo.php b/Foo.php -index abcdefg..abcdefh 100644 ---- a/Foo.php -+++ b/Foo.php -@@ -20,4 +20,5 @@ class Foo - const ONE = 1; - const TWO = 2; -+ const THREE = 3; - const FOUR = 4; diff --git a/tests/v3_0/fixtures/patch2.txt b/tests/v3_0/fixtures/patch2.txt deleted file mode 100644 index 41fbc959..00000000 --- a/tests/v3_0/fixtures/patch2.txt +++ /dev/null @@ -1,21 +0,0 @@ -diff --git a/Foo.php b/Foo.php -index abcdefg..abcdefh 100644 ---- a/Foo.php -+++ b/Foo.php -@@ -20,4 +20,5 @@ class Foo - const ONE = 1; - const TWO = 2; -+ const THREE = 3; - const FOUR = 4; - -@@ -320,4 +320,5 @@ class Foo - const A = 'A'; - const B = 'B'; -+ const C = 'C'; - const D = 'D'; - -@@ -600,4 +600,5 @@ class Foo - public function doSomething() { - -+ return 'foo'; - } From 8f57f0c9d14b172a3440c411ed91325c9336601b Mon Sep 17 00:00:00 2001 From: SpacePossum Date: Tue, 27 Mar 2018 08:53:28 +0200 Subject: [PATCH 15/25] Update composer.json --- composer.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/composer.json b/composer.json index 98eed5ea..af566e1b 100644 --- a/composer.json +++ b/composer.json @@ -12,9 +12,6 @@ { "name": "Kore Nordmann", "email": "mail@kore-nordmann.de" - }, - { - "name": "SpacePossum" } ], "require": { From d4c6b325e9153b497b4fc3bf95b150e8e8886eb5 Mon Sep 17 00:00:00 2001 From: SpacePossum Date: Tue, 27 Mar 2018 08:53:57 +0200 Subject: [PATCH 16/25] Update LICENSE --- LICENSE | 3 --- 1 file changed, 3 deletions(-) diff --git a/LICENSE b/LICENSE index 19a2d69c..a7ebe095 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,2 @@ Code from `sebastian/diff` has been forked and republished by permission of Sebastian Bergmann. Licenced with BSD-3-Clause @ see LICENSE_DIFF, copyright (c) Sebastian Bergmann - -Code from `GeckoPackages/GeckoDiffOutputBuilder` has been copied and republished by permission of GeckoPackages. -Licenced with MIT @ see LICENSE_GECKO, copyright (c) GeckoPackages https://github.com/GeckoPackages From bcefc953fa2eb034bd4467650cb5c3bdc7796b4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Rumi=C5=84ski?= Date: Mon, 20 Aug 2018 11:52:14 +0200 Subject: [PATCH 17/25] Delete ChangeLog.md (#10) Changelog is confusing only, it mention only one release, while there are few more. --- ChangeLog.md | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 ChangeLog.md diff --git a/ChangeLog.md b/ChangeLog.md deleted file mode 100644 index b8767088..00000000 --- a/ChangeLog.md +++ /dev/null @@ -1,7 +0,0 @@ -# ChangeLog - -Changelog for v1.0 - -First stable release, based on: -https://github.com/sebastianbergmann/diff/releases -1.4.3 and 2.0.1 From 1050bd50fdcf43cf953c7de2b80b13d45439fe49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Rumi=C5=84ski?= Date: Wed, 28 Aug 2019 00:17:27 +0200 Subject: [PATCH 18/25] Simplify license (#14) --- LICENSE | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index a7ebe095..93cf0088 100644 --- a/LICENSE +++ b/LICENSE @@ -1,2 +1,31 @@ -Code from `sebastian/diff` has been forked and republished by permission of Sebastian Bergmann. -Licenced with BSD-3-Clause @ see LICENSE_DIFF, copyright (c) Sebastian Bergmann +Copyright (c) 2002-2017, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. From ad7130ccbc2d3626cee6aa676f9fc315e0f1efb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Rumi=C5=84ski?= Date: Wed, 28 Aug 2019 00:17:40 +0200 Subject: [PATCH 19/25] DX: cleanup .gitignore (#12) --- .gitignore | 1 - LICENSE_DIFF | 31 ------------------------------- README.md | 3 ++- 3 files changed, 2 insertions(+), 33 deletions(-) delete mode 100644 LICENSE_DIFF diff --git a/.gitignore b/.gitignore index bf6dd089..5cf9a2cf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ /composer.lock /phpunit.xml /vendor/ -tests/GeckoPackages/DiffOutputBuilder/Tests/Integration/out diff --git a/LICENSE_DIFF b/LICENSE_DIFF deleted file mode 100644 index 93cf0088..00000000 --- a/LICENSE_DIFF +++ /dev/null @@ -1,31 +0,0 @@ -Copyright (c) 2002-2017, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index f38ce3ac..19cddca7 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,8 @@ This version is for PHP CS Fixer only! Do not use it! Code from `sebastian/diff` has been forked a republished by permission of Sebastian Bergmann. -Licenced with BSD-3-Clause @ see LICENSE_DIFF, copyright (c) Sebastian Bergmann +Main change is to make the package compatible with older PHP engines. +Licenced with BSD-3-Clause @ see LICENSE, copyright (c) Sebastian Bergmann https://github.com/sebastianbergmann/diff For questions visit us @ https://gitter.im/PHP-CS-Fixer/Lobby From 0adf6e5f49e342a9171dabf0be9439f116eacb44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Rumi=C5=84ski?= Date: Wed, 4 Sep 2019 12:52:57 +0200 Subject: [PATCH 20/25] Port changes from upstream (#13) --- src/Chunk.php | 14 +++++++++++++- tests/ChunkTest.php | 15 +++++++++++---- tests/LongestCommonSubsequenceTest.php | 2 +- ...ictUnifiedDiffOutputBuilderIntegrationTest.php | 1 + .../Output/StrictUnifiedDiffOutputBuilderTest.php | 2 ++ tests/ParserTest.php | 13 ++++--------- tests/Utils/UnifiedDiffAssertTrait.php | 3 +++ 7 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/Chunk.php b/src/Chunk.php index 317ed867..49c77575 100644 --- a/src/Chunk.php +++ b/src/Chunk.php @@ -33,7 +33,7 @@ final class Chunk private $endRange; /** - * @var array + * @var Line[] */ private $lines; @@ -66,13 +66,25 @@ public function getEndRange() return $this->endRange; } + /** + * @return Line[] + */ public function getLines() { return $this->lines; } + /** + * @param Line[] $lines + */ public function setLines(array $lines) { + foreach ($lines as $line) { + if (!$line instanceof Line) { + throw new InvalidArgumentException; + } + } + $this->lines = $lines; } } diff --git a/tests/ChunkTest.php b/tests/ChunkTest.php index 29fec466..cf038cd0 100644 --- a/tests/ChunkTest.php +++ b/tests/ChunkTest.php @@ -14,6 +14,8 @@ /** * @covers PhpCsFixer\Diff\Chunk + * + * @uses PhpCsFixer\Diff\Line */ final class ChunkTest extends TestCase { @@ -27,6 +29,11 @@ protected function setUp() $this->chunk = new Chunk; } + public function testHasInitiallyNoLines() + { + $this->assertSame([], $this->chunk->getLines()); + } + public function testCanBeCreatedWithoutArguments() { $this->assertInstanceOf(Chunk::class, $this->chunk); @@ -59,10 +66,10 @@ public function testLinesCanBeRetrieved() public function testLinesCanBeSet() { - $this->assertSame([], $this->chunk->getLines()); + $lines = [new Line(Line::ADDED, 'added'), new Line(Line::REMOVED, 'removed')]; + + $this->chunk->setLines($lines); - $testValue = ['line0', 'line1']; - $this->chunk->setLines($testValue); - $this->assertSame($testValue, $this->chunk->getLines()); + $this->assertSame($lines, $this->chunk->getLines()); } } diff --git a/tests/LongestCommonSubsequenceTest.php b/tests/LongestCommonSubsequenceTest.php index 47856642..c49a58f1 100644 --- a/tests/LongestCommonSubsequenceTest.php +++ b/tests/LongestCommonSubsequenceTest.php @@ -35,7 +35,7 @@ abstract class LongestCommonSubsequenceTest extends TestCase protected function setUp() { $this->memoryLimit = \ini_get('memory_limit'); - \ini_set('memory_limit', '256M'); + \ini_set('memory_limit', '-1'); $this->implementation = $this->createImplementation(); } diff --git a/tests/Output/Integration/StrictUnifiedDiffOutputBuilderIntegrationTest.php b/tests/Output/Integration/StrictUnifiedDiffOutputBuilderIntegrationTest.php index 1c28f941..f39d8fea 100644 --- a/tests/Output/Integration/StrictUnifiedDiffOutputBuilderIntegrationTest.php +++ b/tests/Output/Integration/StrictUnifiedDiffOutputBuilderIntegrationTest.php @@ -21,6 +21,7 @@ * * @uses PhpCsFixer\Diff\Differ * @uses PhpCsFixer\Diff\TimeEfficientLongestCommonSubsequenceCalculator + * @uses PhpCsFixer\Diff\MemoryEfficientLongestCommonSubsequenceCalculator * * @requires OS Linux */ diff --git a/tests/Output/StrictUnifiedDiffOutputBuilderTest.php b/tests/Output/StrictUnifiedDiffOutputBuilderTest.php index 0123d9a4..2c770c58 100644 --- a/tests/Output/StrictUnifiedDiffOutputBuilderTest.php +++ b/tests/Output/StrictUnifiedDiffOutputBuilderTest.php @@ -19,6 +19,8 @@ * @covers PhpCsFixer\Diff\Output\StrictUnifiedDiffOutputBuilder * * @uses PhpCsFixer\Diff\Differ + * @uses PhpCsFixer\Diff\TimeEfficientLongestCommonSubsequenceCalculator + * @uses PhpCsFixer\Diff\ConfigurationException */ final class StrictUnifiedDiffOutputBuilderTest extends TestCase { diff --git a/tests/ParserTest.php b/tests/ParserTest.php index 207e23d3..1bbe5968 100644 --- a/tests/ParserTest.php +++ b/tests/ParserTest.php @@ -38,12 +38,10 @@ public function testParse() $diffs = $this->parser->parse($content); - $this->assertInternalType('array', $diffs); $this->assertContainsOnlyInstancesOf(Diff::class, $diffs); $this->assertCount(1, $diffs); $chunks = $diffs[0]->getChunks(); - $this->assertInternalType('array', $chunks); $this->assertContainsOnlyInstancesOf(Chunk::class, $chunks); $this->assertCount(1, $chunks); @@ -75,7 +73,7 @@ public function testParseWithMultipleChunks() public function testParseWithRemovedLines() { - $content = <<parser->parse($content); - $this->assertInternalType('array', $diffs); $this->assertContainsOnlyInstancesOf(Diff::class, $diffs); $this->assertCount(1, $diffs); $chunks = $diffs[0]->getChunks(); - $this->assertInternalType('array', $chunks); $this->assertContainsOnlyInstancesOf(Chunk::class, $chunks); $this->assertCount(1, $chunks); @@ -102,7 +98,6 @@ public function testParseWithRemovedLines() $this->assertSame(8, $chunk->getEndRange()); $lines = $chunk->getLines(); - $this->assertInternalType('array', $lines); $this->assertContainsOnlyInstancesOf(Line::class, $lines); $this->assertCount(2, $lines); @@ -118,7 +113,7 @@ public function testParseWithRemovedLines() public function testParseDiffForMulitpleFiles() { - $content = <<parser->parse($content); $this->assertCount(2, $diffs); diff --git a/tests/Utils/UnifiedDiffAssertTrait.php b/tests/Utils/UnifiedDiffAssertTrait.php index 56093e88..8598fe96 100644 --- a/tests/Utils/UnifiedDiffAssertTrait.php +++ b/tests/Utils/UnifiedDiffAssertTrait.php @@ -27,6 +27,7 @@ public function assertValidUnifiedDiffFormat($diff) // test diff ends with a line break $last = \substr($diff, -1); + if ("\n" !== $last && "\r" !== $last) { throw new \UnexpectedValueException(\sprintf('Expected diff to end with a line break, got "%s".', $last)); } @@ -130,6 +131,7 @@ public function assertValidUnifiedDiffFormat($diff) } $previousType = $this->unifiedDiffAssertLinePrefix($lines[$lineNumber - 2], \sprintf('Preceding line of "\\ No newline at end of file" of unexpected format. Line %d.', $lineNumber)); + if (isset($endOfLineTypes[$previousType])) { throw new \UnexpectedValueException(\sprintf('Unexpected "\\ No newline at end of file", "%s" was already closed. Line %d.', $type, $lineNumber)); } @@ -190,6 +192,7 @@ private function unifiedDiffAssertLinePrefix($line, $message) private function unifiedDiffAssertStrLength($line, $min, $message) { $length = \strlen($line); + if ($length < $min) { throw new \UnexpectedValueException(\sprintf('Expected string length of minimal %d, got %d. %s', $min, $length, $message)); } From 834b52c245240db327d9ab06ade429bfa3f08d5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Rumi=C5=84ski?= Date: Wed, 4 Sep 2019 13:05:18 +0200 Subject: [PATCH 21/25] DX: Add 7.3 to Travis (#11) --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index fbe6693e..0873065b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,8 @@ matrix: env: COMPOSER_FLAGS="--prefer-stable" - php: 7.2 env: COMPOSER_FLAGS="--prefer-stable" + - php: 7.3 + env: COMPOSER_FLAGS="--prefer-stable" - php: nightly env: COMPOSER_FLAGS="--dev --ignore-platform-reqs" allow_failures: From c7fe15b54f18f0872575bfdb88c4846a064e42c8 Mon Sep 17 00:00:00 2001 From: SpacePossum Date: Wed, 15 Jan 2020 14:04:52 +0100 Subject: [PATCH 22/25] Test on PHP 7.4 (#17) --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 0873065b..49e3311b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,6 +18,8 @@ matrix: env: COMPOSER_FLAGS="--prefer-stable" - php: 7.3 env: COMPOSER_FLAGS="--prefer-stable" + - php: 7.4 + env: COMPOSER_FLAGS="--prefer-stable" - php: nightly env: COMPOSER_FLAGS="--dev --ignore-platform-reqs" allow_failures: From 29dc0d507e838c4580d018bd8b5cb412474f7ec3 Mon Sep 17 00:00:00 2001 From: SpacePossum Date: Wed, 14 Oct 2020 10:32:19 +0200 Subject: [PATCH 23/25] PHP8 (#20) --- composer.json | 4 +-- src/Output/UnifiedDiffOutputBuilder.php | 4 +-- .../StrictUnifiedDiffOutputBuilderTest.php | 29 +++++++++++++++++++ tests/Output/UnifiedDiffOutputBuilderTest.php | 24 +++++++++++++++ 4 files changed, 57 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index af566e1b..910267f0 100644 --- a/composer.json +++ b/composer.json @@ -15,10 +15,10 @@ } ], "require": { - "php": "^5.6 || ^7.0" + "php": "^5.6 || ^7.0 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^5.7.23 || ^6.4.3", + "phpunit/phpunit": "^5.7.23 || ^6.4.3 || ^7.0", "symfony/process": "^3.3" }, "config": { diff --git a/src/Output/UnifiedDiffOutputBuilder.php b/src/Output/UnifiedDiffOutputBuilder.php index 69a12708..13e6636a 100644 --- a/src/Output/UnifiedDiffOutputBuilder.php +++ b/src/Output/UnifiedDiffOutputBuilder.php @@ -67,11 +67,11 @@ public function getDiff(array $diff) \fclose($buffer); - // If the last char is not a linebreak: add it. + // If the diff is non-empty and a linebreak: add it. // This might happen when both the `from` and `to` do not have a trailing linebreak $last = \substr($diff, -1); - return "\n" !== $last && "\r" !== $last + return 0 !== \strlen($diff) && "\n" !== $last && "\r" !== $last ? $diff . "\n" : $diff ; diff --git a/tests/Output/StrictUnifiedDiffOutputBuilderTest.php b/tests/Output/StrictUnifiedDiffOutputBuilderTest.php index 2c770c58..17d76ff5 100644 --- a/tests/Output/StrictUnifiedDiffOutputBuilderTest.php +++ b/tests/Output/StrictUnifiedDiffOutputBuilderTest.php @@ -305,6 +305,35 @@ public function testEmptyDiff() ); } + /** + * @param string $from + * @param string $to + * + * @dataProvider provideSameEmptyDiff + */ + public function testSameEmptyDiff($from, $to) + { + $builder = new StrictUnifiedDiffOutputBuilder([ + 'fromFile' => 'input.txt', + 'toFile' => 'output.txt', + ]); + + $differ = new Differ($builder); + + $this->assertSame( + '', + $differ->diff($from, $to) + ); + } + + public function provideSameEmptyDiff() + { + return [ + ['', ''], + ['a', 'a'], + ]; + } + /** * @param array $options * @param string $message diff --git a/tests/Output/UnifiedDiffOutputBuilderTest.php b/tests/Output/UnifiedDiffOutputBuilderTest.php index e3001f53..d05569d8 100644 --- a/tests/Output/UnifiedDiffOutputBuilderTest.php +++ b/tests/Output/UnifiedDiffOutputBuilderTest.php @@ -87,4 +87,28 @@ public function provideDiffWithLineNumbers() { return UnifiedDiffOutputBuilderDataProvider::provideDiffWithLineNumbers(); } + + /** + * @param string $from + * @param string $to + * + * @dataProvider provideStringsThatAreTheSame + */ + public function testEmptyDiffProducesEmptyOutput($from, $to) + { + $differ = new Differ(new UnifiedDiffOutputBuilder('', false)); + $output = $differ->diff($from, $to); + $this->assertEmpty($output); + } + + public function provideStringsThatAreTheSame() + { + return [ + ['', ''], + ['a', 'a'], + ['these strings are the same', 'these strings are the same'], + ["\n", "\n"], + ["multi-line strings\nare the same", "multi-line strings\nare the same"] + ]; + } } From 856de6d8db29dc852cc3c01b1ec760d7b540d1f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Rumi=C5=84ski?= Date: Wed, 4 Dec 2024 02:19:00 +0100 Subject: [PATCH 24/25] Create SECURITY.md --- .github/SECURITY.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .github/SECURITY.md diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 00000000..43444505 --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,5 @@ +# Security Policy + +To report a security issue, please use the GitHub Security Advisory ["Report a Vulnerability"](https://github.com/PHP-CS-Fixer/diff/security/advisories/new) tab. + +Report security bugs in third-party libraries to the person or team maintaining that library. From f919c261fca8fe7e4a1dcfad2777eabf86aaa47d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Rumi=C5=84ski?= Date: Mon, 12 May 2025 00:28:36 +0200 Subject: [PATCH 25/25] Update SECURITY.md --- .github/SECURITY.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/SECURITY.md b/.github/SECURITY.md index 43444505..10f0353f 100644 --- a/.github/SECURITY.md +++ b/.github/SECURITY.md @@ -1,5 +1,7 @@ # Security Policy -To report a security issue, please use the GitHub Security Advisory ["Report a Vulnerability"](https://github.com/PHP-CS-Fixer/diff/security/advisories/new) tab. +DO NOT PUBLISH SECURITY REPORTS PUBLICLY. -Report security bugs in third-party libraries to the person or team maintaining that library. +If you found any issues that might have security implications, please send a report to dariusz.ruminski+php-cs-fixer[at]gmail.com . + +Report security bugs in third-party libraries directly to the group maintaining that library.