Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/testing/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"ext-json": "*",
"ock/class-discovery": "self.version",
"ock/helpers": "self.version",
"fisharebest/algorithm": "^1.6",
"symfony/yaml": "^7.1.5"
},
"require-dev": {
Expand Down
92 changes: 31 additions & 61 deletions packages/testing/src/Diff/ExportedArrayDiffer.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Ock\Testing\Diff;

use Fisharebest\Algorithm\MyersDiff;
use Symfony\Component\Yaml\Tag\TaggedValue;

class ExportedArrayDiffer implements DifferInterface {
Expand Down Expand Up @@ -107,76 +108,45 @@ protected function compareArrays(array $before, array $after, bool $could_be_lis
* @return array|false
*/
protected function compareLists(array $before, array $after): array|false {
$diff = $this->doCompareLists($before, $after);
if (!$diff || count($diff) === count($before) + count($after)) {
// The two lists are completely different.
$algorithm = new MyersDiff();
/** @var list<array{mixed, -1|0|1}> $myers_diff_solution */
$myers_diff_solution = $algorithm->calculate(
// The Myers diff actually works for non-strings.
// See https://github.com/fisharebest/algorithm/pull/9.
// @phpstan-ignore argument.type
$before,
// @phpstan-ignore argument.type
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is no longer needed once this is released:

$after,
fn ($a, $b) => false !== $this->compareValues($a, $b),
);
if (count($myers_diff_solution) >= count($before) + count($after) - 1) {
// The two lists are too different.
return false;
}
return $diff;
}

/**
* Compares two lists recursively.
*
* @param list<mixed> $before
* @param list<mixed> $after
* @param int $i_before
* @param int $i_after
*
* @return array
*/
protected function doCompareLists(array $before, array $after, int $i_before = 0, int $i_after = 0): array {
$diff = [];
while (true) {
if ($i_before >= count($before)) {
// There are more items in "after" list.
for (; $i_after < count($after); ++$i_after) {
$diff[] = new TaggedValue('add', $after[$i_after]);
}
return $diff;
$result = [];
$i_before = 0;
$i_after = 0;
foreach ($myers_diff_solution as [, $operation]) {
if ($operation === -1) {
$result[] = new TaggedValue('--', $before[$i_before]);
++$i_before;
}
if ($i_after >= count($after)) {
// There are more items in "before" list.
for (; $i_before < count($before); ++$i_before) {
$diff[] = new TaggedValue('--', $before[$i_before]);
}
return $diff;
elseif ($operation === 1) {
$result[] = new TaggedValue('add', $after[$i_after]);
++$i_after;
}
$item_diff = $this->compareValues($before[$i_before], $after[$i_after]);
if ($item_diff === []) {
// The two values are the same.
else {
$item_diff = $this->compareValues($before[$i_before], $after[$i_after]);
++$i_before;
++$i_after;
continue;
}
// The two items are completely different.
$diff_minus = $this->doCompareLists($before, $after, $i_before + 1, $i_after);
$diff_plus = $this->doCompareLists($before, $after, $i_before, $i_after + 1);
if ($item_diff !== false) {
$diff_eq = $this->doCompareLists($before, $after, $i_before + 1, $i_after + 1);
if (count($diff_eq) < count($diff_minus) && count($diff_eq) < count($diff_plus)) {
return [
...$diff,
new TaggedValue('diff', $item_diff),
...$diff_eq,
];
#assert($item_diff !== false);
if ($item_diff === []) {
continue;
}
}
if (count($diff_minus) <= count($diff_plus)) {
return [
...$diff,
new TaggedValue('--', $before[$i_before]),
...$diff_minus,
];
}
else {
return [
...$diff,
new TaggedValue('add', $after[$i_after]),
...$diff_plus,
];
$result[] = new TaggedValue('diff', $item_diff);
}
}
return $result;
}

protected function compareAssoc(array $before, array $after): array|false {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ after:
b: 'B orig'
- after
diff:
- !--
a: 'A orig'
b: 'B orig'
- !add
a: 'A changed'
b: 'B changed'
- !diff
'~- a': 'A orig'
'~+ a': 'A changed'
- !--
a: 'A orig'
b: 'B orig'
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,9 @@ after:
- goodbye
- world
diff:
- !-- hello
- !add goodbye
'-':
- hello
- world
+:
- goodbye
- world
Original file line number Diff line number Diff line change
Expand Up @@ -40,32 +40,44 @@ after:
- '@drupal.proxy_original_service.paramconverter.menu_link'
- drupal.proxy_original_service.paramconverter.menu_link
diff:
- !add
- addConverter
'-':
-
- addConverter
-
class: Symfony\Component\DependencyInjection\Definition
getClass(): Drupal\ock\UI\ParamConverter\ParamConverter_Iface
getTags():
paramconverter:
- { }
- Drupal\ock\UI\ParamConverter\ParamConverter_Iface
- !add
- addConverter
- '@drupal.proxy_original_service.paramconverter.menu_link'
- drupal.proxy_original_service.paramconverter.menu_link
+:
-
- addConverter
-
class: Symfony\Component\DependencyInjection\Definition
getArguments():
- '@Ock\Ock\Plugin\Map\PluginMapInterface'
-
class: Symfony\Component\DependencyInjection\TypedReference
$type: Psr\Log\LoggerInterface
$attributes:
-
class: Symfony\Component\DependencyInjection\Attribute\Autowire
$value: '@logger.channel.ock'
getClass(): Drupal\ock\UI\ParamConverter\ParamConverter_Plugin
getTags():
paramconverter:
- { }
- Drupal\ock\UI\ParamConverter\ParamConverter_Plugin
-
class: Symfony\Component\DependencyInjection\Definition
getClass(): Drupal\ock\UI\ParamConverter\ParamConverter_Iface
getTags():
paramconverter:
- { }
- Drupal\ock\UI\ParamConverter\ParamConverter_Iface
-
- addConverter
-
-
class: Symfony\Component\DependencyInjection\Definition
getArguments():
- '@Ock\Ock\Plugin\Map\PluginMapInterface'
-
class: Symfony\Component\DependencyInjection\TypedReference
$type: Psr\Log\LoggerInterface
$attributes:
-
class: Symfony\Component\DependencyInjection\Attribute\Autowire
$value: '@logger.channel.ock'
getClass(): Drupal\ock\UI\ParamConverter\ParamConverter_Plugin
getTags():
paramconverter:
- { }
- Drupal\ock\UI\ParamConverter\ParamConverter_Plugin
-
- addConverter
-
- '@drupal.proxy_original_service.paramconverter.menu_link'
- drupal.proxy_original_service.paramconverter.menu_link