pax_global_header00006660000000000000000000000064137677160530014531gustar00rootroot0000000000000052 comment=2a97187eb192d94d11c4ed6b46f7906a1fc380e8 phpunit-diff-4.0.4/000077500000000000000000000000001376771605300141335ustar00rootroot00000000000000phpunit-diff-4.0.4/.github/000077500000000000000000000000001376771605300154735ustar00rootroot00000000000000phpunit-diff-4.0.4/.github/FUNDING.yml000066400000000000000000000000321376771605300173030ustar00rootroot00000000000000github: sebastianbergmann phpunit-diff-4.0.4/.github/workflows/000077500000000000000000000000001376771605300175305ustar00rootroot00000000000000phpunit-diff-4.0.4/.github/workflows/ci.yml000066400000000000000000000053121376771605300206470ustar00rootroot00000000000000# https://help.github.com/en/categories/automating-your-workflow-with-github-actions on: - "pull_request" - "push" name: "CI" jobs: coding-guidelines: name: "Coding Guidelines" runs-on: "ubuntu-latest" steps: - name: "Checkout" uses: "actions/checkout@v2" - name: "Run friendsofphp/php-cs-fixer" run: "php7.4 ./tools/php-cs-fixer fix --diff-format=udiff --dry-run --show-progress=dots --using-cache=no --verbose" type-checker: name: "Type Checker" runs-on: "ubuntu-latest" steps: - name: "Checkout" uses: "actions/checkout@v2" - name: "Update dependencies with composer" run: "php7.4 ./tools/composer update --no-ansi --no-interaction --no-progress" - name: "Run vimeo/psalm" run: "php7.4 ./tools/psalm --config=.psalm/config.xml --no-progress --shepherd --show-info=false --stats" backward-compatibility: name: Backward Compatibility runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v2 with: fetch-depth: 0 - name: Fetch tags run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* - name: Install PHP with extensions uses: shivammathur/setup-php@v2 with: php-version: 7.4 coverage: none extensions: intl - name: Run roave/backward-compatibility-check run: ./tools/roave-backward-compatibility-check --from=4.0.4 tests: name: "Tests" runs-on: "ubuntu-latest" strategy: fail-fast: false matrix: php-version: - "7.3" - "7.4" - "8.0" - "8.1" steps: - name: "Checkout" uses: "actions/checkout@v2" - name: "Install PHP with extensions" uses: "shivammathur/setup-php@v2" with: php-version: "${{ matrix.php-version }}" coverage: "pcov" ini-values: memory_limit=-1 - name: "Cache dependencies installed with composer" uses: "actions/cache@v1" with: path: "~/.composer/cache" key: "php${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-${{ hashFiles('**/composer.json') }}" restore-keys: "php${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-" - name: "Install dependencies with composer" run: "./tools/composer update --no-ansi --no-interaction --no-progress" - name: "Run tests with phpunit/phpunit" run: "vendor/bin/phpunit --coverage-clover=coverage.xml" - name: "Send code coverage report to Codecov.io" env: CODECOV_TOKEN: "${{ secrets.CODECOV_TOKEN }}" run: "bash <(curl -s https://codecov.io/bash) || true" phpunit-diff-4.0.4/.gitignore000066400000000000000000000001331376771605300161200ustar00rootroot00000000000000/.idea /.php_cs /.php_cs.cache /.phpunit.result.cache /.psalm/cache /composer.lock /vendor phpunit-diff-4.0.4/.phive/000077500000000000000000000000001376771605300153245ustar00rootroot00000000000000phpunit-diff-4.0.4/.phive/phars.xml000066400000000000000000000006671376771605300171740ustar00rootroot00000000000000 phpunit-diff-4.0.4/.php_cs.dist000066400000000000000000000173661376771605300163670ustar00rootroot00000000000000 For the full copyright and license information, please view the LICENSE file that was distributed with this source code. EOF; $finder = PhpCsFixer\Finder::create() ->files() ->in(__DIR__ . '/src') ->in(__DIR__ . '/tests'); return PhpCsFixer\Config::create() ->setFinder($finder) ->setRiskyAllowed(true) ->setRules([ 'align_multiline_comment' => true, 'array_indentation' => true, 'array_syntax' => ['syntax' => 'short'], 'binary_operator_spaces' => [ 'operators' => [ '=' => 'align', '=>' => 'align', ], ], 'blank_line_after_namespace' => true, 'blank_line_before_statement' => [ 'statements' => [ 'break', 'continue', 'declare', 'do', 'for', 'foreach', 'if', 'include', 'include_once', 'require', 'require_once', 'return', 'switch', 'throw', 'try', 'while', 'yield', ], ], 'braces' => true, 'cast_spaces' => true, 'class_attributes_separation' => ['elements' => ['const', 'method', 'property']], 'combine_consecutive_issets' => true, 'combine_consecutive_unsets' => true, 'compact_nullable_typehint' => true, 'concat_space' => ['spacing' => 'one'], 'declare_equal_normalize' => ['space' => 'none'], 'declare_strict_types' => true, 'dir_constant' => true, 'elseif' => true, 'encoding' => true, 'full_opening_tag' => true, 'function_declaration' => true, 'global_namespace_import' => [ 'import_classes' => true, 'import_constants' => true, 'import_functions' => true, ], 'header_comment' => ['header' => $header, 'separate' => 'none'], 'indentation_type' => true, 'is_null' => true, 'line_ending' => true, 'list_syntax' => ['syntax' => 'short'], 'logical_operators' => true, 'lowercase_cast' => true, 'lowercase_constants' => true, 'lowercase_keywords' => true, 'lowercase_static_reference' => true, 'magic_constant_casing' => true, 'method_argument_space' => ['ensure_fully_multiline' => true], 'modernize_types_casting' => true, 'multiline_comment_opening_closing' => true, 'multiline_whitespace_before_semicolons' => true, 'native_constant_invocation' => false, 'native_function_casing' => false, 'native_function_invocation' => false, 'new_with_braces' => false, 'no_alias_functions' => true, 'no_alternative_syntax' => true, 'no_blank_lines_after_class_opening' => true, 'no_blank_lines_after_phpdoc' => true, 'no_blank_lines_before_namespace' => true, 'no_closing_tag' => true, 'no_empty_comment' => true, 'no_empty_phpdoc' => true, 'no_empty_statement' => true, 'no_extra_blank_lines' => true, 'no_homoglyph_names' => true, 'no_leading_import_slash' => true, 'no_leading_namespace_whitespace' => true, 'no_mixed_echo_print' => ['use' => 'print'], 'no_multiline_whitespace_around_double_arrow' => true, 'no_null_property_initialization' => true, 'no_php4_constructor' => true, 'no_short_bool_cast' => true, 'no_short_echo_tag' => true, 'no_singleline_whitespace_before_semicolons' => true, 'no_spaces_after_function_name' => true, 'no_spaces_inside_parenthesis' => true, 'no_superfluous_elseif' => true, 'no_superfluous_phpdoc_tags' => [ 'allow_mixed' => true, ], 'no_trailing_comma_in_list_call' => true, 'no_trailing_comma_in_singleline_array' => true, 'no_trailing_whitespace' => true, 'no_trailing_whitespace_in_comment' => true, 'no_unneeded_control_parentheses' => true, 'no_unneeded_curly_braces' => true, 'no_unneeded_final_method' => true, 'no_unreachable_default_argument_value' => true, 'no_unset_on_property' => true, 'no_unused_imports' => true, 'no_useless_else' => true, 'no_useless_return' => true, 'no_whitespace_before_comma_in_array' => true, 'no_whitespace_in_blank_line' => true, 'non_printable_character' => true, 'normalize_index_brace' => true, 'object_operator_without_whitespace' => true, 'ordered_class_elements' => [ 'order' => [ 'use_trait', 'constant_public', 'constant_protected', 'constant_private', 'property_public_static', 'property_protected_static', 'property_private_static', 'property_public', 'property_protected', 'property_private', 'method_public_static', 'construct', 'destruct', 'magic', 'phpunit', 'method_public', 'method_protected', 'method_private', 'method_protected_static', 'method_private_static', ], ], 'ordered_imports' => [ 'imports_order' => [ PhpCsFixer\Fixer\Import\OrderedImportsFixer::IMPORT_TYPE_CONST, PhpCsFixer\Fixer\Import\OrderedImportsFixer::IMPORT_TYPE_FUNCTION, PhpCsFixer\Fixer\Import\OrderedImportsFixer::IMPORT_TYPE_CLASS, ] ], 'ordered_interfaces' => [ 'direction' => 'ascend', 'order' => 'alpha', ], 'phpdoc_add_missing_param_annotation' => false, 'phpdoc_align' => true, 'phpdoc_annotation_without_dot' => true, 'phpdoc_indent' => true, 'phpdoc_no_access' => true, 'phpdoc_no_empty_return' => true, 'phpdoc_no_package' => true, 'phpdoc_order' => true, 'phpdoc_return_self_reference' => true, 'phpdoc_scalar' => true, 'phpdoc_separation' => true, 'phpdoc_single_line_var_spacing' => true, 'phpdoc_summary' => true, 'phpdoc_to_comment' => true, 'phpdoc_trim' => true, 'phpdoc_trim_consecutive_blank_line_separation' => true, 'phpdoc_types' => ['groups' => ['simple', 'meta']], 'phpdoc_types_order' => true, 'phpdoc_var_without_name' => true, 'pow_to_exponentiation' => true, 'protected_to_private' => true, 'return_assignment' => true, 'return_type_declaration' => ['space_before' => 'none'], 'self_accessor' => true, 'semicolon_after_instruction' => true, 'set_type_to_cast' => true, 'short_scalar_cast' => true, 'simplified_null_return' => false, 'single_blank_line_at_eof' => true, 'single_import_per_statement' => true, 'single_line_after_imports' => true, 'single_quote' => true, 'standardize_not_equals' => true, 'ternary_to_null_coalescing' => true, 'trailing_comma_in_multiline_array' => true, 'trim_array_spaces' => true, 'unary_operator_spaces' => true, 'visibility_required' => [ 'elements' => [ 'const', 'method', 'property', ], ], 'void_return' => true, 'whitespace_after_comma_in_array' => true, ]); phpunit-diff-4.0.4/.psalm/000077500000000000000000000000001376771605300153255ustar00rootroot00000000000000phpunit-diff-4.0.4/.psalm/baseline.xml000066400000000000000000000026661376771605300176430ustar00rootroot00000000000000 $line instanceof Line is_array($from) is_array($to) $input $line null === $outputBuilder $value $output $output $default $changed $output $output phpunit-diff-4.0.4/.psalm/config.xml000066400000000000000000000010151376771605300173110ustar00rootroot00000000000000 phpunit-diff-4.0.4/ChangeLog.md000066400000000000000000000046611376771605300163130ustar00rootroot00000000000000# ChangeLog All notable changes are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles. ## [4.0.4] - 2020-10-26 ### Fixed * `SebastianBergmann\Diff\Exception` now correctly extends `\Throwable` ## [4.0.3] - 2020-09-28 ### Changed * Changed PHP version constraint in `composer.json` from `^7.3 || ^8.0` to `>=7.3` ## [4.0.2] - 2020-06-30 ### Added * This component is now supported on PHP 8 ## [4.0.1] - 2020-05-08 ### Fixed * [#99](https://github.com/sebastianbergmann/diff/pull/99): Regression in unified diff output of identical strings ## [4.0.0] - 2020-02-07 ### Removed * Removed support for PHP 7.1 and PHP 7.2 ## [3.0.2] - 2019-02-04 ### Changed * `Chunk::setLines()` now ensures that the `$lines` array only contains `Line` objects ## [3.0.1] - 2018-06-10 ### Fixed * Removed `"minimum-stability": "dev",` from `composer.json` ## [3.0.0] - 2018-02-01 * The `StrictUnifiedDiffOutputBuilder` implementation of the `DiffOutputBuilderInterface` was added ### Changed * The default `DiffOutputBuilderInterface` implementation now generates context lines (unchanged lines) ### Removed * Removed support for PHP 7.0 ### Fixed * [#70](https://github.com/sebastianbergmann/diff/issues/70): Diffing of arrays no longer works ## [2.0.1] - 2017-08-03 ### 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 * [#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 [4.0.4]: https://github.com/sebastianbergmann/diff/compare/4.0.3...4.0.4 [4.0.3]: https://github.com/sebastianbergmann/diff/compare/4.0.2...4.0.3 [4.0.2]: https://github.com/sebastianbergmann/diff/compare/4.0.1...4.0.2 [4.0.1]: https://github.com/sebastianbergmann/diff/compare/4.0.0...4.0.1 [4.0.0]: https://github.com/sebastianbergmann/diff/compare/3.0.2...4.0.0 [3.0.2]: https://github.com/sebastianbergmann/diff/compare/3.0.1...3.0.2 [3.0.1]: https://github.com/sebastianbergmann/diff/compare/3.0.0...3.0.1 [3.0.0]: https://github.com/sebastianbergmann/diff/compare/2.0...3.0.0 [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 phpunit-diff-4.0.4/LICENSE000066400000000000000000000030151376771605300151370ustar00rootroot00000000000000sebastian/diff Copyright (c) 2002-2020, 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. phpunit-diff-4.0.4/README.md000066400000000000000000000170431376771605300154170ustar00rootroot00000000000000# sebastian/diff [![CI Status](https://github.com/sebastianbergmann/diff/workflows/CI/badge.svg)](https://github.com/sebastianbergmann/diff/actions) [![Type Coverage](https://shepherd.dev/github/sebastianbergmann/diff/coverage.svg)](https://shepherd.dev/github/sebastianbergmann/diff) Diff implementation for PHP, factored out of PHPUnit into a stand-alone component. ## Installation 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 #### Generating diff The `Differ` class can be used to generate a textual representation of the difference between two strings: ```php diff('foo', 'bar'); ``` The code above yields the output below: ```diff --- Original +++ New @@ @@ -foo +bar ``` There are three output builders available in this package: #### UnifiedDiffOutputBuilder This is default builder, which generates the output close to udiff and is used by PHPUnit. ```php diff('foo', 'bar'); ``` #### StrictUnifiedDiffOutputBuilder Generates (strict) Unified diff's (unidiffs) with hunks, similar to `diff -u` and compatible with `patch` and `git apply`. ```php 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, ]); $differ = new Differ($builder); print $differ->diff('foo', 'bar'); ``` #### DiffOnlyOutputBuilder Output only the lines that differ. ```php diff('foo', 'bar'); ``` #### DiffOutputBuilderInterface You can pass any output builder to the `Differ` class as longs as it implements the `DiffOutputBuilderInterface`. #### Parsing diff 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')); ) ) ) ) ) ) phpunit-diff-4.0.4/build.xml000066400000000000000000000020461376771605300157560ustar00rootroot00000000000000 phpunit-diff-4.0.4/composer.json000066400000000000000000000020211376771605300166500ustar00rootroot00000000000000{ "name": "sebastian/diff", "description": "Diff implementation", "keywords": ["diff", "udiff", "unidiff", "unified diff"], "homepage": "https://github.com/sebastianbergmann/diff", "license": "BSD-3-Clause", "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, { "name": "Kore Nordmann", "email": "mail@kore-nordmann.de" } ], "prefer-stable": true, "config": { "platform": { "php": "7.3.0" }, "optimize-autoloader": true, "sort-packages": true }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3", "symfony/process": "^4.2 || ^5" }, "autoload": { "classmap": [ "src/" ] }, "autoload-dev": { "classmap": [ "tests/" ] }, "extra": { "branch-alias": { "dev-master": "4.0-dev" } } } phpunit-diff-4.0.4/phpunit.xml000066400000000000000000000015121376771605300163430ustar00rootroot00000000000000 tests src phpunit-diff-4.0.4/src/000077500000000000000000000000001376771605300147225ustar00rootroot00000000000000phpunit-diff-4.0.4/src/Chunk.php000066400000000000000000000031541376771605300165060ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; final class Chunk { /** * @var int */ private $start; /** * @var int */ private $startRange; /** * @var int */ private $end; /** * @var int */ private $endRange; /** * @var Line[] */ private $lines; public function __construct(int $start = 0, int $startRange = 1, int $end = 0, int $endRange = 1, array $lines = []) { $this->start = $start; $this->startRange = $startRange; $this->end = $end; $this->endRange = $endRange; $this->lines = $lines; } public function getStart(): int { return $this->start; } public function getStartRange(): int { return $this->startRange; } public function getEnd(): int { return $this->end; } public function getEndRange(): int { return $this->endRange; } /** * @return Line[] */ public function getLines(): array { return $this->lines; } /** * @param Line[] $lines */ public function setLines(array $lines): void { foreach ($lines as $line) { if (!$line instanceof Line) { throw new InvalidArgumentException; } } $this->lines = $lines; } } phpunit-diff-4.0.4/src/Diff.php000066400000000000000000000021511376771605300163020ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; final class Diff { /** * @var string */ private $from; /** * @var string */ private $to; /** * @var Chunk[] */ private $chunks; /** * @param Chunk[] $chunks */ public function __construct(string $from, string $to, array $chunks = []) { $this->from = $from; $this->to = $to; $this->chunks = $chunks; } public function getFrom(): string { return $this->from; } public function getTo(): string { return $this->to; } /** * @return Chunk[] */ public function getChunks(): array { return $this->chunks; } /** * @param Chunk[] $chunks */ public function setChunks(array $chunks): void { $this->chunks = $chunks; } } phpunit-diff-4.0.4/src/Differ.php000066400000000000000000000223441376771605300166370ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; use const PHP_INT_SIZE; use const PREG_SPLIT_DELIM_CAPTURE; use const PREG_SPLIT_NO_EMPTY; use function array_shift; use function array_unshift; use function array_values; use function count; use function current; use function end; use function get_class; use function gettype; use function is_array; use function is_object; use function is_string; use function key; use function min; use function preg_split; use function prev; use function reset; use function sprintf; use function substr; use SebastianBergmann\Diff\Output\DiffOutputBuilderInterface; use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder; final class Differ { public const OLD = 0; public const ADDED = 1; public const REMOVED = 2; public const DIFF_LINE_END_WARNING = 3; public 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 */ public function diff($from, $to, LongestCommonSubsequenceCalculator $lcs = null): string { $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 */ public function diffToArray($from, $to, LongestCommonSubsequenceCalculator $lcs = null): array { 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.'); } [$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, ["#Warning: Strings contain different line endings!\n", self::DIFF_LINE_END_WARNING]); } return $diff; } /** * Casts variable to string if it is not a string or array. * * @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. */ private function splitStringByLines(string $input): array { return preg_split('/(.*\R)/', $input, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); } private function selectLcsImplementation(array $from, array $to): LongestCommonSubsequenceCalculator { // 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. * * @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. */ private function detectUnmatchedLineEndings(array $diff): bool { $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): string { 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): array { $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]; } } phpunit-diff-4.0.4/src/Exception/000077500000000000000000000000001376771605300166605ustar00rootroot00000000000000phpunit-diff-4.0.4/src/Exception/ConfigurationException.php000066400000000000000000000017321376771605300240620ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; use function get_class; use function gettype; use function is_object; use function sprintf; use Exception; final class ConfigurationException extends InvalidArgumentException { public function __construct( string $option, string $expected, $value, int $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 ); } } phpunit-diff-4.0.4/src/Exception/Exception.php000066400000000000000000000005411376771605300213270ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; use Throwable; interface Exception extends Throwable { } phpunit-diff-4.0.4/src/Exception/InvalidArgumentException.php000066400000000000000000000006011376771605300243360ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; class InvalidArgumentException extends \InvalidArgumentException implements Exception { } phpunit-diff-4.0.4/src/Line.php000066400000000000000000000015201376771605300163200ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; final class Line { public const ADDED = 1; public const REMOVED = 2; public const UNCHANGED = 3; /** * @var int */ private $type; /** * @var string */ private $content; public function __construct(int $type = self::UNCHANGED, string $content = '') { $this->type = $type; $this->content = $content; } public function getContent(): string { return $this->content; } public function getType(): int { return $this->type; } } phpunit-diff-4.0.4/src/LongestCommonSubsequenceCalculator.php000066400000000000000000000007461376771605300244430ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; interface LongestCommonSubsequenceCalculator { /** * Calculates the longest common subsequence of two arrays. */ public function calculate(array $from, array $to): array; } phpunit-diff-4.0.4/src/MemoryEfficientLongestCommonSubsequenceCalculator.php000066400000000000000000000043551376771605300274510ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; use function array_fill; use function array_merge; use function array_reverse; use function array_slice; use function count; use function in_array; use function max; final class MemoryEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator { /** * {@inheritdoc} */ public function calculate(array $from, array $to): array { $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): array { $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; } } phpunit-diff-4.0.4/src/Output/000077500000000000000000000000001376771605300162225ustar00rootroot00000000000000phpunit-diff-4.0.4/src/Output/AbstractChunkOutputBuilder.php000066400000000000000000000030021376771605300242120ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff\Output; use function count; abstract class AbstractChunkOutputBuilder implements DiffOutputBuilderInterface { /** * Takes input of the diff array and returns the common parts. * Iterates through diff line by line. */ protected function getCommonChunks(array $diff, int $lineThreshold = 5): array { $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; } } phpunit-diff-4.0.4/src/Output/DiffOnlyOutputBuilder.php000066400000000000000000000040621376771605300231770ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff\Output; use function fclose; use function fopen; use function fwrite; use function stream_get_contents; use function substr; use SebastianBergmann\Diff\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(string $header = "--- Original\n+++ New\n") { $this->header = $header; } public function getDiff(array $diff): string { $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; } } phpunit-diff-4.0.4/src/Output/DiffOutputBuilderInterface.php000066400000000000000000000010111376771605300241450ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff\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): string; } phpunit-diff-4.0.4/src/Output/StrictUnifiedDiffOutputBuilder.php000066400000000000000000000253421376771605300250360ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff\Output; use function array_merge; use function array_splice; use function count; use function fclose; use function fopen; use function fwrite; use function is_bool; use function is_int; use function is_string; use function max; use function min; use function sprintf; use function stream_get_contents; use function substr; use SebastianBergmann\Diff\ConfigurationException; use SebastianBergmann\Diff\Differ; /** * Strict Unified diff output builder. * * Generates (strict) Unified diff's (unidiffs) with hunks. */ final class StrictUnifiedDiffOutputBuilder implements DiffOutputBuilderInterface { 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, ]; /** * @var bool */ private $changed; /** * @var bool */ private $collapseRanges; /** * @var int >= 0 */ private $commonLineThreshold; /** * @var string */ private $header; /** * @var int >= 0 */ private $contextLines; 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']); } $this->assertString($options, 'fromFile'); $this->assertString($options, 'toFile'); $this->assertStringOrNull($options, 'fromFileDate'); $this->assertStringOrNull($options, 'toFileDate'); $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): string { 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): void { // 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; $i = 0; /** @var int $i */ 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, int $diffStartIndex, int $diffEndIndex, int $fromStart, int $fromRange, int $toStart, int $toRange, $output ): void { 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 //} } } private function assertString(array $options, string $option): void { if (!is_string($options[$option])) { throw new ConfigurationException($option, 'a string', $options[$option]); } } private function assertStringOrNull(array $options, string $option): void { if (null !== $options[$option] && !is_string($options[$option])) { throw new ConfigurationException($option, 'a string or ', $options[$option]); } } } phpunit-diff-4.0.4/src/Output/UnifiedDiffOutputBuilder.php000066400000000000000000000204461376771605300236450ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff\Output; use function array_splice; use function count; use function fclose; use function fopen; use function fwrite; use function max; use function min; use function stream_get_contents; use function strlen; use function substr; use SebastianBergmann\Diff\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(string $header = "--- Original\n+++ New\n", bool $addLineNumbers = false) { $this->header = $header; $this->addLineNumbers = $addLineNumbers; } public function getDiff(array $diff): string { $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 diff is non-empty and 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 0 !== strlen($diff) && "\n" !== $last && "\r" !== $last ? $diff . "\n" : $diff; } private function writeDiffHunks($output, array $diff): void { // 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; $i = 0; /** @var int $i */ 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, int $diffStartIndex, int $diffEndIndex, int $fromStart, int $fromRange, int $toStart, int $toRange, $output ): void { 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]); } } } } phpunit-diff-4.0.4/src/Parser.php000066400000000000000000000060261376771605300166730ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; use function array_pop; use function count; use function max; use function preg_match; use function preg_split; /** * Unified diff parser. */ final class Parser { /** * @return Diff[] */ public function parse(string $string): array { $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('#^---\h+"?(?P[^\\v\\t"]+)#', $lines[$i], $fromMatch) && preg_match('#^\\+\\+\\+\\h+"?(?P[^\\v\\t"]+)#', $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): void { $chunks = []; $chunk = null; $diffLines = []; 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); } } phpunit-diff-4.0.4/src/TimeEfficientLongestCommonSubsequenceCalculator.php000066400000000000000000000035341376771605300270750ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; use function array_reverse; use function count; use function max; use SplFixedArray; final class TimeEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator { /** * {@inheritdoc} */ public function calculate(array $from, array $to): array { $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); } } phpunit-diff-4.0.4/tests/000077500000000000000000000000001376771605300152755ustar00rootroot00000000000000phpunit-diff-4.0.4/tests/ChunkTest.php000066400000000000000000000032731376771605300177230ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; use PHPUnit\Framework\TestCase; /** * @covers \SebastianBergmann\Diff\Chunk * * @uses \SebastianBergmann\Diff\Line */ final class ChunkTest extends TestCase { /** * @var Chunk */ private $chunk; protected function setUp(): void { $this->chunk = new Chunk; } public function testHasInitiallyNoLines(): void { $this->assertSame([], $this->chunk->getLines()); } public function testCanBeCreatedWithoutArguments(): void { $this->assertInstanceOf(Chunk::class, $this->chunk); } public function testStartCanBeRetrieved(): void { $this->assertSame(0, $this->chunk->getStart()); } public function testStartRangeCanBeRetrieved(): void { $this->assertSame(1, $this->chunk->getStartRange()); } public function testEndCanBeRetrieved(): void { $this->assertSame(0, $this->chunk->getEnd()); } public function testEndRangeCanBeRetrieved(): void { $this->assertSame(1, $this->chunk->getEndRange()); } public function testLinesCanBeRetrieved(): void { $this->assertSame([], $this->chunk->getLines()); } public function testLinesCanBeSet(): void { $lines = [new Line(Line::ADDED, 'added'), new Line(Line::REMOVED, 'removed')]; $this->chunk->setLines($lines); $this->assertSame($lines, $this->chunk->getLines()); } } phpunit-diff-4.0.4/tests/DiffTest.php000066400000000000000000000031661376771605300175240ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; use PHPUnit\Framework\TestCase; /** * @covers \SebastianBergmann\Diff\Diff * * @uses \SebastianBergmann\Diff\Chunk */ final class DiffTest extends TestCase { public function testGettersAfterConstructionWithDefault(): void { $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(): void { $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(): void { $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.'); } } phpunit-diff-4.0.4/tests/DifferTest.php000066400000000000000000000266611376771605300200600ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; use PHPUnit\Framework\TestCase; use ReflectionObject; use SplFileInfo; use stdClass; /** * @covers \SebastianBergmann\Diff\Differ * @covers \SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder * * @uses \SebastianBergmann\Diff\MemoryEfficientLongestCommonSubsequenceCalculator * @uses \SebastianBergmann\Diff\TimeEfficientLongestCommonSubsequenceCalculator * @uses \SebastianBergmann\Diff\Output\AbstractChunkOutputBuilder */ final class DifferTest extends TestCase { /** * @var Differ */ private $differ; protected function setUp(): void { $this->differ = new Differ; } /** * @param array|string $from * @param array|string $to * * @dataProvider arrayProvider */ public function testArrayRepresentationOfDiffCanBeRenderedUsingTimeEfficientLcsImplementation(array $expected, $from, $to): void { $this->assertSame($expected, $this->differ->diffToArray($from, $to, new TimeEfficientLongestCommonSubsequenceCalculator)); } /** * @dataProvider textProvider */ public function testTextRepresentationOfDiffCanBeRenderedUsingTimeEfficientLcsImplementation(string $expected, string $from, string $to): void { $this->assertSame($expected, $this->differ->diff($from, $to, new TimeEfficientLongestCommonSubsequenceCalculator)); } /** * @param array|string $from * @param array|string $to * * @dataProvider arrayProvider */ public function testArrayRepresentationOfDiffCanBeRenderedUsingMemoryEfficientLcsImplementation(array $expected, $from, $to): void { $this->assertSame($expected, $this->differ->diffToArray($from, $to, new MemoryEfficientLongestCommonSubsequenceCalculator)); } /** * @dataProvider textProvider */ public function testTextRepresentationOfDiffCanBeRenderedUsingMemoryEfficientLcsImplementation(string $expected, string $from, string $to): void { $this->assertSame($expected, $this->differ->diff($from, $to, new MemoryEfficientLongestCommonSubsequenceCalculator)); } public function testTypesOtherThanArrayAndStringCanBePassed(): void { $this->assertSame( "--- Original\n+++ New\n@@ @@\n-1\n+2\n", $this->differ->diff(1, 2) ); } public function testArrayDiffs(): void { $this->assertSame( '--- Original +++ New @@ @@ -one +two ', $this->differ->diff(['one'], ['two']) ); } public function arrayProvider(): array { 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' => [ [ [ "#Warning: Strings contain different line endings!\n", Differ::DIFF_LINE_END_WARNING, ], [ " [ [ [ "#Warning: Strings contain different line endings!\n", Differ::DIFF_LINE_END_WARNING, ], [ "expectException(InvalidArgumentException::class); $this->expectExceptionMessageMatches('#^"from" must be an array or string\.$#'); $this->differ->diffToArray(null, ''); } public function testDiffInvalidToType(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessageMatches('#^"to" must be an array or string\.$#'); $this->differ->diffToArray('', new stdClass); } /** * @dataProvider provideSplitStringByLinesCases */ public function testSplitStringByLines(array $expected, string $input): void { $reflection = new ReflectionObject($this->differ); $method = $reflection->getMethod('splitStringByLines'); $method->setAccessible(true); $this->assertSame($expected, $method->invoke($this->differ, $input)); } public function provideSplitStringByLinesCases(): array { 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 testConstructorInvalidArgInt(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessageMatches('/^Expected builder to be an instance of DiffOutputBuilderInterface, or a string, got integer "1"\.$/'); new Differ(1); } public function testConstructorInvalidArgObject(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessageMatches('/^Expected builder to be an instance of DiffOutputBuilderInterface, or a string, got instance of "SplFileInfo"\.$/'); new Differ(new SplFileInfo(__FILE__)); } } phpunit-diff-4.0.4/tests/Exception/000077500000000000000000000000001376771605300172335ustar00rootroot00000000000000phpunit-diff-4.0.4/tests/Exception/ConfigurationExceptionTest.php000066400000000000000000000022111376771605300252660ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; use BadMethodCallException; use PHPUnit\Framework\TestCase; use SplFileInfo; /** * @covers \SebastianBergmann\Diff\ConfigurationException */ final class ConfigurationExceptionTest extends TestCase { public function testConstructWithDefaults(): void { $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(): void { $e = new ConfigurationException( 'test', 'integer', new SplFileInfo(__FILE__), 789, new BadMethodCallException(__METHOD__) ); $this->assertSame('Option "test" must be integer, got "SplFileInfo".', $e->getMessage()); } } phpunit-diff-4.0.4/tests/Exception/InvalidArgumentExceptionTest.php000066400000000000000000000017701376771605300255610ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; use LogicException; use PHPUnit\Framework\TestCase; /** * @covers \SebastianBergmann\Diff\InvalidArgumentException */ final class InvalidArgumentExceptionTest extends TestCase { public function testInvalidArgumentException(): void { $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()); } } phpunit-diff-4.0.4/tests/LineTest.php000066400000000000000000000016561376771605300175450ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; use PHPUnit\Framework\TestCase; /** * @covers \SebastianBergmann\Diff\Line */ final class LineTest extends TestCase { /** * @var Line */ private $line; protected function setUp(): void { $this->line = new Line; } public function testCanBeCreatedWithoutArguments(): void { $this->assertInstanceOf(Line::class, $this->line); } public function testTypeCanBeRetrieved(): void { $this->assertSame(Line::UNCHANGED, $this->line->getType()); } public function testContentCanBeRetrieved(): void { $this->assertSame('', $this->line->getContent()); } } phpunit-diff-4.0.4/tests/LongestCommonSubsequenceTest.php000066400000000000000000000132501376771605300236360ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; use function array_reverse; use function array_slice; use function ini_get; use function ini_set; use function range; 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(): void { $this->memoryLimit = ini_get('memory_limit'); ini_set('memory_limit', '-1'); $this->implementation = $this->createImplementation(); } protected function tearDown(): void { ini_set('memory_limit', $this->memoryLimit); } public function testBothEmpty(): void { $from = []; $to = []; $common = $this->implementation->calculate($from, $to); $this->assertSame([], $common); } public function testIsStrictComparison(): void { $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(): void { 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(): void { $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(): void { $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(): void { 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(): void { 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(): void { 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(): void { $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(): void { $diff = $this->implementation->calculate(['5'], ['05']); $this->assertIsArray($diff); $this->assertCount(0, $diff); } abstract protected function createImplementation(): LongestCommonSubsequenceCalculator; } phpunit-diff-4.0.4/tests/MemoryEfficientImplementationTest.php000066400000000000000000000011771376771605300246470ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; /** * @covers \SebastianBergmann\Diff\MemoryEfficientLongestCommonSubsequenceCalculator */ final class MemoryEfficientImplementationTest extends LongestCommonSubsequenceTest { protected function createImplementation(): LongestCommonSubsequenceCalculator { return new MemoryEfficientLongestCommonSubsequenceCalculator; } } phpunit-diff-4.0.4/tests/Output/000077500000000000000000000000001376771605300165755ustar00rootroot00000000000000phpunit-diff-4.0.4/tests/Output/AbstractChunkOutputBuilderTest.php000066400000000000000000000102561376771605300254360ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff\Output; use PHPUnit\Framework\TestCase; use SebastianBergmann\Diff\Differ; /** * @covers \SebastianBergmann\Diff\Output\AbstractChunkOutputBuilder * * @uses \SebastianBergmann\Diff\Differ * @uses \SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder * @uses \SebastianBergmann\Diff\TimeEfficientLongestCommonSubsequenceCalculator */ final class AbstractChunkOutputBuilderTest extends TestCase { /** * @dataProvider provideGetCommonChunks */ public function testGetCommonChunks(array $expected, string $from, string $to, int $lineThreshold = 5): void { $output = new class extends AbstractChunkOutputBuilder { public function getDiff(array $diff): string { return ''; } public function getChunks(array $diff, $lineThreshold) { return $this->getCommonChunks($diff, $lineThreshold); } }; $this->assertSame( $expected, $output->getChunks((new Differ)->diffToArray($from, $to), $lineThreshold) ); } public function provideGetCommonChunks(): array { 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", ], ]; } } phpunit-diff-4.0.4/tests/Output/DiffOnlyOutputBuilderTest.php000066400000000000000000000033571376771605300244200ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff\Output; use PHPUnit\Framework\TestCase; use SebastianBergmann\Diff\Differ; /** * @covers \SebastianBergmann\Diff\Output\DiffOnlyOutputBuilder * * @uses \SebastianBergmann\Diff\Differ * @uses \SebastianBergmann\Diff\TimeEfficientLongestCommonSubsequenceCalculator */ final class DiffOnlyOutputBuilderTest extends TestCase { /** * @dataProvider textForNoNonDiffLinesProvider */ public function testDiffDoNotShowNonDiffLines(string $expected, string $from, string $to, string $header = ''): void { $differ = new Differ(new DiffOnlyOutputBuilder($header)); $this->assertSame($expected, $differ->diff($from, $to)); } public function textForNoNonDiffLinesProvider(): array { return [ [ " #Warning: Strings 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", ], ]; } } phpunit-diff-4.0.4/tests/Output/Integration/000077500000000000000000000000001376771605300210605ustar00rootroot00000000000000phpunit-diff-4.0.4/tests/Output/Integration/StrictUnifiedDiffOutputBuilderIntegrationTest.php000066400000000000000000000237461376771605300327460ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff\Output; use const PREG_SPLIT_DELIM_CAPTURE; use const PREG_SPLIT_NO_EMPTY; use function file_put_contents; use function implode; use function is_dir; use function preg_replace; use function preg_split; use function realpath; use function sprintf; use function unlink; use PHPUnit\Framework\TestCase; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; use RuntimeException; use SebastianBergmann\Diff\Differ; use SebastianBergmann\Diff\Utils\FileUtils; use SebastianBergmann\Diff\Utils\UnifiedDiffAssertTrait; use SplFileInfo; use Symfony\Component\Process\Process; /** * @covers \SebastianBergmann\Diff\Output\StrictUnifiedDiffOutputBuilder * * @uses \SebastianBergmann\Diff\Differ * @uses \SebastianBergmann\Diff\TimeEfficientLongestCommonSubsequenceCalculator * @uses \SebastianBergmann\Diff\MemoryEfficientLongestCommonSubsequenceCalculator * * @requires OS Linux */ final class StrictUnifiedDiffOutputBuilderIntegrationTest extends TestCase { use UnifiedDiffAssertTrait; private $dir; private $fileFrom; private $fileTo; private $filePatch; protected function setUp(): void { $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(): void { $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` * * @dataProvider provideFilePairs */ public function testIntegrationUsingPHPFileInVendorGitApply(string $fileFrom, string $fileTo): void { $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` * * @dataProvider provideFilePairs */ public function testIntegrationUsingPHPFileInVendorPatch(string $fileFrom, string $fileTo): void { $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); } /** * @dataProvider provideOutputBuildingCases * @dataProvider provideSample * @dataProvider provideBasicDiffGeneration */ public function testIntegrationOfUnitTestCasesGitApply(string $expected, string $from, string $to): void { $this->doIntegrationTestGitApply($expected, $from, $to); } /** * @dataProvider provideOutputBuildingCases * @dataProvider provideSample * @dataProvider provideBasicDiffGeneration */ public function testIntegrationOfUnitTestCasesPatch(string $expected, string $from, string $to): void { $this->doIntegrationTestPatch($expected, $from, $to); } public function provideOutputBuildingCases(): array { return StrictUnifiedDiffOutputBuilderDataProvider::provideOutputBuildingCases(); } public function provideSample(): array { return StrictUnifiedDiffOutputBuilderDataProvider::provideSample(); } public function provideBasicDiffGeneration(): array { return StrictUnifiedDiffOutputBuilderDataProvider::provideBasicDiffGeneration(); } public function provideFilePairs(): array { $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. * * @dataProvider provideBasicDiffGeneration */ public function testIntegrationDiffOutputBuilderVersusDiffCommand(string $diff, string $from, string $to): void { $this->assertNotSame('', $diff); $this->assertValidUnifiedDiffFormat($diff); $this->assertNotFalse(file_put_contents($this->fileFrom, $from)); $this->assertNotFalse(file_put_contents($this->fileTo, $to)); $p = Process::fromShellCommandline('diff -u $from $to'); $p->run( null, [ 'from' => $this->fileFrom, 'to' => $this->fileTo, ] ); $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(string $diff, string $from, string $to): void { $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 = Process::fromShellCommandline('git --git-dir $dir apply --check -v --unsafe-paths --ignore-whitespace $patch'); $p->run( null, [ 'dir' => $this->dir, 'patch' => $this->filePatch, ] ); $this->assertProcessSuccessful($p); } private function doIntegrationTestPatch(string $diff, string $from, string $to): void { $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 = Process::fromShellCommandline('patch -u --verbose --posix $from < $patch'); $p->run( null, [ 'from' => $this->fileFrom, 'patch' => $this->filePatch, ] ); $this->assertProcessSuccessful($p); $this->assertStringEqualsFile( $this->fileFrom, $to, sprintf('Patch command "%s".', $p->getCommandLine()) ); } private function assertProcessSuccessful(Process $p): void { $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(): void { @unlink($this->fileFrom . '.orig'); @unlink($this->fileFrom . '.rej'); @unlink($this->fileFrom); @unlink($this->fileTo); @unlink($this->filePatch); } private static function setDiffFileHeader(string $diff, string $file): string { $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); } } phpunit-diff-4.0.4/tests/Output/Integration/UnifiedDiffOutputBuilderIntegrationTest.php000066400000000000000000000115601376771605300315440ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff\Output; use const ARRAY_FILTER_USE_KEY; use const PREG_SPLIT_DELIM_CAPTURE; use const PREG_SPLIT_NO_EMPTY; use function array_filter; use function file_put_contents; use function implode; use function is_string; use function preg_replace; use function preg_split; use function realpath; use function sprintf; use function strpos; use function unlink; use PHPUnit\Framework\TestCase; use SebastianBergmann\Diff\Utils\UnifiedDiffAssertTrait; use Symfony\Component\Process\Process; /** * @covers \SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder * * @uses \SebastianBergmann\Diff\Differ * @uses \SebastianBergmann\Diff\TimeEfficientLongestCommonSubsequenceCalculator * * @requires OS Linux */ final class UnifiedDiffOutputBuilderIntegrationTest extends TestCase { use UnifiedDiffAssertTrait; private $dir; private $fileFrom; private $filePatch; protected function setUp(): void { $this->dir = realpath(__DIR__ . '/../../fixtures/out/') . '/'; $this->fileFrom = $this->dir . 'from.txt'; $this->filePatch = $this->dir . 'patch.txt'; $this->cleanUpTempFiles(); } protected function tearDown(): void { $this->cleanUpTempFiles(); } /** * @dataProvider provideDiffWithLineNumbers */ public function testDiffWithLineNumbersPath($expected, $from, $to): void { $this->doIntegrationTestPatch($expected, $from, $to); } /** * @dataProvider provideDiffWithLineNumbers */ public function testDiffWithLineNumbersGitApply($expected, $from, $to): void { $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(string $diff, string $from, string $to): void { $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 = Process::fromShellCommandline('patch -u --verbose --posix $from < $patch'); // --posix $p->run( null, [ 'from' => $this->fileFrom, 'patch' => $this->filePatch, ] ); $this->assertProcessSuccessful($p); $this->assertStringEqualsFile( $this->fileFrom, $to, sprintf('Patch command "%s".', $p->getCommandLine()) ); } private function doIntegrationTestGitApply(string $diff, string $from, string $to): void { $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 = Process::fromShellCommandline('git --git-dir $dir apply --check -v --unsafe-paths --ignore-whitespace $patch'); $p->run( null, [ 'dir' => $this->dir, 'patch' => $this->filePatch, ] ); $this->assertProcessSuccessful($p); } private function assertProcessSuccessful(Process $p): void { $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(): void { @unlink($this->fileFrom . '.orig'); @unlink($this->fileFrom); @unlink($this->filePatch); } private static function setDiffFileHeader(string $diff, string $file): string { $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); } } phpunit-diff-4.0.4/tests/Output/StrictUnifiedDiffOutputBuilderDataProvider.php000066400000000000000000000067251376771605300277220ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff\Output; final class StrictUnifiedDiffOutputBuilderDataProvider { public static function provideOutputBuildingCases(): array { 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(): array { 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(): array { 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', ], ]; } } phpunit-diff-4.0.4/tests/Output/StrictUnifiedDiffOutputBuilderTest.php000066400000000000000000000411721376771605300262500ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff\Output; use function array_merge; use function preg_quote; use function sprintf; use function substr; use function time; use PHPUnit\Framework\TestCase; use SebastianBergmann\Diff\ConfigurationException; use SebastianBergmann\Diff\Differ; use SebastianBergmann\Diff\Utils\UnifiedDiffAssertTrait; use SplFileInfo; /** * @covers \SebastianBergmann\Diff\Output\StrictUnifiedDiffOutputBuilder * * @uses \SebastianBergmann\Diff\Differ * @uses \SebastianBergmann\Diff\TimeEfficientLongestCommonSubsequenceCalculator * @uses \SebastianBergmann\Diff\ConfigurationException */ final class StrictUnifiedDiffOutputBuilderTest extends TestCase { use UnifiedDiffAssertTrait; /** * @dataProvider provideOutputBuildingCases */ public function testOutputBuilding(string $expected, string $from, string $to, array $options): void { $diff = $this->getDiffer($options)->diff($from, $to); $this->assertValidDiffFormat($diff); $this->assertSame($expected, $diff); } /** * @dataProvider provideSample */ public function testSample(string $expected, string $from, string $to, array $options): void { $diff = $this->getDiffer($options)->diff($from, $to); $this->assertValidDiffFormat($diff); $this->assertSame($expected, $diff); } /** * {@inheritdoc} */ public function assertValidDiffFormat(string $diff): void { $this->assertValidUnifiedDiffFormat($diff); } /** * {@inheritdoc} */ public function provideOutputBuildingCases(): array { return StrictUnifiedDiffOutputBuilderDataProvider::provideOutputBuildingCases(); } /** * {@inheritdoc} */ public function provideSample(): array { return StrictUnifiedDiffOutputBuilderDataProvider::provideSample(); } /** * @dataProvider provideBasicDiffGeneration */ public function testBasicDiffGeneration(string $expected, string $from, string $to): void { $diff = $this->getDiffer([ 'fromFile' => 'input.txt', 'toFile' => 'output.txt', ])->diff($from, $to); $this->assertValidDiffFormat($diff); $this->assertSame($expected, $diff); } public function provideBasicDiffGeneration(): array { return StrictUnifiedDiffOutputBuilderDataProvider::provideBasicDiffGeneration(); } /** * @dataProvider provideConfiguredDiffGeneration */ public function testConfiguredDiffGeneration(string $expected, string $from, string $to, array $config = []): void { $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(): array { 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(): void { $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(): void { $builder = new StrictUnifiedDiffOutputBuilder([ 'fromFile' => 'input.txt', 'toFile' => 'output.txt', ]); $this->assertSame( '', $builder->getDiff([]) ); } /** * @dataProvider provideInvalidConfiguration */ public function testInvalidConfiguration(array $options, string $message): void { $this->expectException(ConfigurationException::class); $this->expectExceptionMessageMatches(sprintf('#^%s$#', preg_quote($message, '#'))); new StrictUnifiedDiffOutputBuilder($options); } public function provideInvalidConfiguration(): array { $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 "".', ], ]; } /** * @dataProvider provideCommonLineThresholdCases */ public function testCommonLineThreshold(string $expected, string $from, string $to, int $threshold): void { $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(): array { 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, ], ]; } /** * @dataProvider provideContextLineConfigurationCases */ public function testContextLineConfiguration(string $expected, string $from, string $to, int $contextLines, int $commonLineThreshold = 6): void { $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(): array { $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. */ private function getDiffer(array $options = []): Differ { return new Differ(new StrictUnifiedDiffOutputBuilder($options)); } } phpunit-diff-4.0.4/tests/Output/UnifiedDiffOutputBuilderDataProvider.php000066400000000000000000000133541376771605300265250ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff\Output; final class UnifiedDiffOutputBuilderDataProvider { public static function provideDiffWithLineNumbers(): array { 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 #Warning: Strings 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 SebastianBergmann\Diff\Output; use PHPUnit\Framework\TestCase; use SebastianBergmann\Diff\Differ; /** * @covers \SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder * * @uses \SebastianBergmann\Diff\Differ * @uses \SebastianBergmann\Diff\Output\AbstractChunkOutputBuilder * @uses \SebastianBergmann\Diff\TimeEfficientLongestCommonSubsequenceCalculator */ final class UnifiedDiffOutputBuilderTest extends TestCase { /** * @dataProvider headerProvider */ public function testCustomHeaderCanBeUsed(string $expected, string $from, string $to, string $header): void { $differ = new Differ(new UnifiedDiffOutputBuilder($header)); $this->assertSame( $expected, $differ->diff($from, $to) ); } public function headerProvider(): array { 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', '', ], ]; } /** * @dataProvider provideDiffWithLineNumbers */ public function testDiffWithLineNumbers(string $expected, string $from, string $to): void { $differ = new Differ(new UnifiedDiffOutputBuilder("--- Original\n+++ New\n", true)); $this->assertSame($expected, $differ->diff($from, $to)); } public function provideDiffWithLineNumbers(): array { return UnifiedDiffOutputBuilderDataProvider::provideDiffWithLineNumbers(); } /** * @dataProvider provideStringsThatAreTheSame */ public function testEmptyDiffProducesEmptyOutput(string $from, string $to): void { $differ = new Differ(new UnifiedDiffOutputBuilder('', false)); $output = $differ->diff($from, $to); $this->assertEmpty($output); } public function provideStringsThatAreTheSame(): array { 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"], ]; } } phpunit-diff-4.0.4/tests/ParserTest.php000066400000000000000000000136101376771605300201030ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; use function unserialize; use PHPUnit\Framework\TestCase; use SebastianBergmann\Diff\Utils\FileUtils; /** * @covers \SebastianBergmann\Diff\Parser * * @uses \SebastianBergmann\Diff\Chunk * @uses \SebastianBergmann\Diff\Diff * @uses \SebastianBergmann\Diff\Line */ final class ParserTest extends TestCase { /** * @var Parser */ private $parser; protected function setUp(): void { $this->parser = new Parser; } public function testParse(): void { $content = FileUtils::getFileContent(__DIR__ . '/fixtures/patch.txt'); $diffs = $this->parser->parse($content); $this->assertContainsOnlyInstancesOf(Diff::class, $diffs); $this->assertCount(1, $diffs); $chunks = $diffs[0]->getChunks(); $this->assertContainsOnlyInstancesOf(Chunk::class, $chunks); $this->assertCount(1, $chunks); $this->assertSame(20, $chunks[0]->getStart()); $this->assertCount(4, $chunks[0]->getLines()); } public function testParseWithMultipleChunks(): void { $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 testParseWithSpacesInFileNames(): void { $content = <<parser->parse($content); $this->assertEquals('a/Foo Bar.txt', $diffs[0]->getFrom()); $this->assertEquals('b/Foo Bar.txt', $diffs[0]->getTo()); } public function testParseWithSpacesInFileNamesAndTimesamp(): void { $content = <<parser->parse($content); $this->assertEquals('a/Foo Bar.txt', $diffs[0]->getFrom()); $this->assertEquals('b/Foo Bar.txt', $diffs[0]->getTo()); } public function testParseWithRemovedLines(): void { $content = <<parser->parse($content); $this->assertContainsOnlyInstancesOf(Diff::class, $diffs); $this->assertCount(1, $diffs); $chunks = $diffs[0]->getChunks(); $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->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(): void { $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()); } /** * @psalm-param list $expected * * @dataProvider diffProvider */ public function testParser(string $diff, array $expected): void { $result = $this->parser->parse($diff); $this->assertEquals($expected, $result); } public function diffProvider(): array { return [ [ "--- old.txt 2014-11-04 08:51:02.661868729 +0300\n+++ new.txt 2014-11-04 08:51:02.665868730 +0300\n@@ -1,3 +1,4 @@\n+2222111\n 1111111\n 1111111\n 1111111\n@@ -5,10 +6,8 @@\n 1111111\n 1111111\n 1111111\n +1121211\n 1111111\n -1111111\n -1111111\n -2222222\n 2222222\n 2222222\n 2222222\n@@ -17,5 +16,6 @@\n 2222222\n 2222222\n 2222222\n +2122212\n 2222222\n 2222222\n", unserialize(FileUtils::getFileContent(__DIR__ . '/fixtures/serialized_diff.bin')), ], ]; } } phpunit-diff-4.0.4/tests/TimeEfficientImplementationTest.php000066400000000000000000000011711376771605300242670ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; /** * @covers \SebastianBergmann\Diff\TimeEfficientLongestCommonSubsequenceCalculator */ final class TimeEfficientImplementationTest extends LongestCommonSubsequenceTest { protected function createImplementation(): LongestCommonSubsequenceCalculator { return new TimeEfficientLongestCommonSubsequenceCalculator; } } phpunit-diff-4.0.4/tests/Utils/000077500000000000000000000000001376771605300163755ustar00rootroot00000000000000phpunit-diff-4.0.4/tests/Utils/FileUtils.php000066400000000000000000000015411376771605300210070ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff\Utils; use function error_get_last; use function file_get_contents; use function sprintf; use RuntimeException; final class FileUtils { public static function getFileContent(string $file): string { $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; } } phpunit-diff-4.0.4/tests/Utils/UnifiedDiffAssertTrait.php000066400000000000000000000255761376771605300234670ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff\Utils; use const PREG_SPLIT_DELIM_CAPTURE; use const PREG_SPLIT_NO_EMPTY; use function count; use function preg_match; use function preg_split; use function sprintf; use function strlen; use function strpos; use function substr; use RuntimeException; use UnexpectedValueException; trait UnifiedDiffAssertTrait { /** * @throws UnexpectedValueException */ public function assertValidUnifiedDiffFormat(string $diff): void { 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; [$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); } /** * @return string '+', '-', '@', ' ' or '\' */ private function unifiedDiffAssertLinePrefix(string $line, string $message): string { $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(string $line, int $min, string $message): void { $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" */ private function unifiedDiffAssertHeaderLine(string $line, string $start, string $message): void { 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(string $date, string $message): void { // 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; } /** * @return int[] */ private function unifiedDiffAssertHunkHeader(string $line, string $message): array { 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), ]; } } phpunit-diff-4.0.4/tests/Utils/UnifiedDiffAssertTraitIntegrationTest.php000066400000000000000000000102041376771605300265110ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff\Utils; use function file_exists; use function realpath; use function sprintf; use function strlen; use function substr; use function unlink; use PHPUnit\Framework\TestCase; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; use SplFileInfo; use Symfony\Component\Process\Process; /** * @requires OS Linux * * @coversNothing */ final class UnifiedDiffAssertTraitIntegrationTest extends TestCase { use UnifiedDiffAssertTrait; private $filePatch; protected function setUp(): void { $this->filePatch = __DIR__ . '/../fixtures/out/patch.txt'; $this->cleanUpTempFiles(); } protected function tearDown(): void { $this->cleanUpTempFiles(); } /** * @dataProvider provideFilePairsCases */ public function testValidPatches(string $fileFrom, string $fileTo): void { $p = Process::fromShellCommandline('diff -u $from $to > $patch'); $p->run( null, [ 'from' => realpath($fileFrom), 'to' => realpath($fileTo), 'patch' => $this->filePatch, ] ); $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", $p->getCommandLine(), $p->getOutput(), $p->getErrorOutput(), $p->getExitCode() ) ); $this->assertValidUnifiedDiffFormat(FileUtils::getFileContent($this->filePatch)); } /** * @return array> */ public function provideFilePairsCases(): array { $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(): void { @unlink($this->filePatch); } } phpunit-diff-4.0.4/tests/Utils/UnifiedDiffAssertTraitTest.php000066400000000000000000000265251376771605300243220ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff\Utils; use function preg_quote; use function sprintf; use PHPUnit\Framework\TestCase; use UnexpectedValueException; /** * @covers \SebastianBergmann\Diff\Utils\UnifiedDiffAssertTrait */ final class UnifiedDiffAssertTraitTest extends TestCase { use UnifiedDiffAssertTrait; /** * @dataProvider provideValidCases */ public function testValidCases(string $diff): void { $this->assertValidUnifiedDiffFormat($diff); } public function provideValidCases(): array { return [ [ '--- Original +++ New @@ -8 +8 @@ -Z +U ', ], [ '--- Original +++ New @@ -8 +8 @@ -Z +U @@ -15 +15 @@ -X +V ', ], 'empty diff. is valid' => [ '', ], ]; } public function testNoLinebreakEnd(): void { $this->expectException(UnexpectedValueException::class); $this->expectExceptionMessageMatches(sprintf('#^%s$#', preg_quote('Expected diff to end with a line break, got "C".', '#'))); $this->assertValidUnifiedDiffFormat("A\nB\nC"); } public function testInvalidStartWithoutHeader(): void { $this->expectException(UnexpectedValueException::class); $this->expectExceptionMessageMatches(sprintf('#^%s$#', preg_quote("Expected line to start with '@', '-' or '+', got \"A\n\". Line 1.", '#'))); $this->assertValidUnifiedDiffFormat("A\n"); } public function testInvalidStartHeader1(): void { $this->expectException(UnexpectedValueException::class); $this->expectExceptionMessageMatches(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(): void { $this->expectException(UnexpectedValueException::class); $this->expectExceptionMessageMatches(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(): void { $this->expectException(UnexpectedValueException::class); $this->expectExceptionMessageMatches(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(): void { $this->expectException(UnexpectedValueException::class); $this->expectExceptionMessageMatches(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(): void { $this->expectException(UnexpectedValueException::class); $this->expectExceptionMessageMatches(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(): void { $this->expectException(UnexpectedValueException::class); $this->expectExceptionMessageMatches(sprintf('#^%s$#', preg_quote('Expected string length of minimal 2, got 1. Line 4.', '#'))); $this->assertValidUnifiedDiffFormat( '--- Original +++ New @@ -8 +8 @@ ' ); } public function testHunkInvalidFormat(): void { $this->expectException(UnexpectedValueException::class); $this->expectExceptionMessageMatches(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(): void { $this->expectException(UnexpectedValueException::class); $this->expectExceptionMessageMatches(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(): void { $this->expectException(UnexpectedValueException::class); $this->expectExceptionMessageMatches(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(): void { $this->expectException(UnexpectedValueException::class); $this->expectExceptionMessageMatches(sprintf('#^%s$#', preg_quote('Expected hunk start (\'@\'), got "+". Line 6.', '#'))); $this->assertValidUnifiedDiffFormat( '--- Original +++ New @@ -8 +8 @@ -Z +U +O ' ); } public function testExpectHunk2(): void { $this->expectException(UnexpectedValueException::class); $this->expectExceptionMessageMatches(sprintf('#^%s$#', preg_quote('Unexpected hunk start (\'@\'). Line 6.', '#'))); $this->assertValidUnifiedDiffFormat( '--- Original +++ New @@ -8,12 +8,12 @@ ' . ' ' . ' @@ -38,12 +48,12 @@ ' ); } public function testMisplacedLineAfterComments1(): void { $this->expectException(UnexpectedValueException::class); $this->expectExceptionMessageMatches(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(): void { $this->expectException(UnexpectedValueException::class); $this->expectExceptionMessageMatches(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(): void { $this->expectException(UnexpectedValueException::class); $this->expectExceptionMessageMatches(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(): void { $this->expectException(UnexpectedValueException::class); $this->expectExceptionMessageMatches(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(): void { $this->expectException(UnexpectedValueException::class); $this->expectExceptionMessageMatches(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(): void { $this->expectException(UnexpectedValueException::class); $this->expectExceptionMessageMatches(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(): void { $this->expectException(UnexpectedValueException::class); $this->expectExceptionMessageMatches(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(): void { $this->expectException(UnexpectedValueException::class); $this->expectExceptionMessageMatches(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(): void { $this->expectException(UnexpectedValueException::class); $this->expectExceptionMessageMatches(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(): void { $this->expectException(UnexpectedValueException::class); $this->expectExceptionMessageMatches(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(): void { $this->expectException(UnexpectedValueException::class); $this->expectExceptionMessageMatches(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(): void { $this->expectException(UnexpectedValueException::class); $this->expectExceptionMessageMatches(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 ' . ' ' ); } } phpunit-diff-4.0.4/tests/fixtures/000077500000000000000000000000001376771605300171465ustar00rootroot00000000000000phpunit-diff-4.0.4/tests/fixtures/.editorconfig000066400000000000000000000000141376771605300216160ustar00rootroot00000000000000root = true phpunit-diff-4.0.4/tests/fixtures/UnifiedDiffAssertTraitIntegrationTest/000077500000000000000000000000001376771605300265545ustar00rootroot00000000000000phpunit-diff-4.0.4/tests/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_a.txt000066400000000000000000000000011376771605300277440ustar00rootroot00000000000000aphpunit-diff-4.0.4/tests/fixtures/UnifiedDiffAssertTraitIntegrationTest/1_b.txt000066400000000000000000000000001376771605300277440ustar00rootroot00000000000000phpunit-diff-4.0.4/tests/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_a.txt000066400000000000000000000001051376771605300277520ustar00rootroot00000000000000a 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 aphpunit-diff-4.0.4/tests/fixtures/UnifiedDiffAssertTraitIntegrationTest/2_b.txt000066400000000000000000000000431376771605300277540ustar00rootroot00000000000000a a a a a a a a a a b a a a a a a cphpunit-diff-4.0.4/tests/fixtures/out/000077500000000000000000000000001376771605300177555ustar00rootroot00000000000000phpunit-diff-4.0.4/tests/fixtures/out/.editorconfig000066400000000000000000000000141376771605300224250ustar00rootroot00000000000000root = true phpunit-diff-4.0.4/tests/fixtures/out/.gitignore000066400000000000000000000001041376771605300217400ustar00rootroot00000000000000# reset all ignore rules to create sandbox for integration test !/**phpunit-diff-4.0.4/tests/fixtures/patch.txt000066400000000000000000000003101376771605300210000ustar00rootroot00000000000000diff --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; phpunit-diff-4.0.4/tests/fixtures/patch2.txt000066400000000000000000000006321376771605300210710ustar00rootroot00000000000000diff --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'; } phpunit-diff-4.0.4/tests/fixtures/serialized_diff.bin000066400000000000000000000100571376771605300227660ustar00rootroot00000000000000a:1:{i:0;O:27:"SebastianBergmann\Diff\Diff":3:{s:33:"SebastianBergmann\Diff\Difffrom";s:7:"old.txt";s:31:"SebastianBergmann\Diff\Diffto";s:7:"new.txt";s:35:"SebastianBergmann\Diff\Diffchunks";a:3:{i:0;O:28:"SebastianBergmann\Diff\Chunk":5:{s:35:"SebastianBergmann\Diff\Chunkstart";i:1;s:40:"SebastianBergmann\Diff\ChunkstartRange";i:3;s:33:"SebastianBergmann\Diff\Chunkend";i:1;s:38:"SebastianBergmann\Diff\ChunkendRange";i:4;s:35:"SebastianBergmann\Diff\Chunklines";a:4:{i:0;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:1;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"2222111";}i:1;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"1111111";}i:2;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"1111111";}i:3;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"1111111";}}}i:1;O:28:"SebastianBergmann\Diff\Chunk":5:{s:35:"SebastianBergmann\Diff\Chunkstart";i:5;s:40:"SebastianBergmann\Diff\ChunkstartRange";i:10;s:33:"SebastianBergmann\Diff\Chunkend";i:6;s:38:"SebastianBergmann\Diff\ChunkendRange";i:8;s:35:"SebastianBergmann\Diff\Chunklines";a:11:{i:0;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"1111111";}i:1;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"1111111";}i:2;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"1111111";}i:3;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:8:"+1121211";}i:4;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"1111111";}i:5;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:8:"-1111111";}i:6;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:8:"-1111111";}i:7;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:8:"-2222222";}i:8;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"2222222";}i:9;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"2222222";}i:10;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"2222222";}}}i:2;O:28:"SebastianBergmann\Diff\Chunk":5:{s:35:"SebastianBergmann\Diff\Chunkstart";i:17;s:40:"SebastianBergmann\Diff\ChunkstartRange";i:5;s:33:"SebastianBergmann\Diff\Chunkend";i:16;s:38:"SebastianBergmann\Diff\ChunkendRange";i:6;s:35:"SebastianBergmann\Diff\Chunklines";a:6:{i:0;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"2222222";}i:1;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"2222222";}i:2;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"2222222";}i:3;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:8:"+2122212";}i:4;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"2222222";}i:5;O:27:"SebastianBergmann\Diff\Line":2:{s:33:"SebastianBergmann\Diff\Linetype";i:3;s:36:"SebastianBergmann\Diff\Linecontent";s:7:"2222222";}}}}}}