From 7bd2ce38add9f0c57931d7e716fb01e287e51c3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= <5175937+theofidry@users.noreply.github.com> Date: Fri, 12 Apr 2024 20:44:21 +0200 Subject: [PATCH] test: Force mutators to include remedies (#1954) This PR is a rebased version of #1907. It currently skips all but one mutator to set a baseline. Existing mutators should no longer be skipped in the future and this check ensures that new mutators will require a remedy to be documented. There is more to do with remedies to, for example some ideas of https://github.com/infection/infection/pull/1907#issuecomment-2043212338, but I think it is vastly out of scope of this PR and should be considered at a later time. Closes #1907. --------- Co-authored-by: Tom de Wit --- src/Mutator/Unwrap/UnwrapArrayMap.php | 8 +- tests/phpunit/Mutator/DefinitionTest.php | 206 +++++++++++++++++++++++ 2 files changed, 213 insertions(+), 1 deletion(-) diff --git a/src/Mutator/Unwrap/UnwrapArrayMap.php b/src/Mutator/Unwrap/UnwrapArrayMap.php index 428c57d89..f8d073abf 100644 --- a/src/Mutator/Unwrap/UnwrapArrayMap.php +++ b/src/Mutator/Unwrap/UnwrapArrayMap.php @@ -64,7 +64,13 @@ public static function getDefinition(): ?Definition TXT , MutatorCategory::SEMANTIC_REDUCTION, - null, + <<<'TXT' + This mutation escaping suggests that the `$callback` transformation passed to + `array_map()` does no effect to the passed elements. + + Either this transformation is needed in which case additional tests capturing + this need are required, or it is not and should be removed. + TXT, <<<'DIFF' - $x = array_map($callback, ['foo', 'bar', 'baz']); + $x = ['foo', 'bar', 'baz']; diff --git a/tests/phpunit/Mutator/DefinitionTest.php b/tests/phpunit/Mutator/DefinitionTest.php index 2c1e28ec1..aff3b901e 100644 --- a/tests/phpunit/Mutator/DefinitionTest.php +++ b/tests/phpunit/Mutator/DefinitionTest.php @@ -35,15 +35,185 @@ namespace Infection\Tests\Mutator; +use function array_diff_key; +use function array_fill_keys; +use function array_flip; use Infection\Mutator\Definition; +use Infection\Mutator\Mutator; use Infection\Mutator\MutatorCategory; +use Infection\Mutator\ProfileList; +use Infection\Tests\SingletonContainer; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; +use function sprintf; #[CoversClass(Definition::class)] final class DefinitionTest extends TestCase { + // TODO: address those + private const MUTATORS_WITHOUT_REMEDIES = [ + 'Assignment', + 'AssignmentEqual', + 'BitwiseAnd', + 'BitwiseNot', + 'BitwiseOr', + 'BitwiseXor', + 'Decrement', + 'DivEqual', + 'Division', + 'Exponentiation', + 'Increment', + 'Minus', + 'MinusEqual', + 'ModEqual', + 'Modulus', + 'MulEqual', + 'Multiplication', + 'Plus', + 'PlusEqual', + 'PowEqual', + 'RoundingFamily', + 'ShiftLeft', + 'ShiftRight', + 'ArrayItem', + 'EqualIdentical', + 'FalseValue', + 'IdenticalEqual', + 'InstanceOf_', + 'LogicalAnd', + 'LogicalAndAllSubExprNegation', + 'LogicalAndNegation', + 'LogicalAndSingleSubExprNegation', + 'LogicalLowerAnd', + 'LogicalLowerOr', + 'LogicalNot', + 'LogicalOr', + 'LogicalOrAllSubExprNegation', + 'LogicalOrNegation', + 'LogicalOrSingleSubExprNegation', + 'NotEqualNotIdentical', + 'NotIdenticalNotEqual', + 'TrueValue', + 'Yield_', + 'GreaterThan', + 'GreaterThanOrEqualTo', + 'LessThan', + 'LessThanOrEqualTo', + 'Equal', + 'GreaterThanNegotiation', + 'GreaterThanOrEqualToNegotiation', + 'Identical', + 'LessThanNegotiation', + 'LessThanOrEqualToNegotiation', + 'NotEqual', + 'NotIdentical', + 'ProtectedVisibility', + 'PublicVisibility', + 'DecrementInteger', + 'IncrementInteger', + 'OneZeroFloat', + 'AssignCoalesce', + 'Break_', + 'Coalesce', + 'Concat', + 'Continue_', + 'ElseIfNegation', + 'Finally_', + 'IfNegation', + 'NullSafeMethodCall', + 'NullSafePropertyCall', + 'SpreadAssignment', + 'SpreadOneItem', + 'SpreadRemoval', + 'Ternary', + 'Throw_', + 'Catch_', + 'PregMatchMatches', + 'PregMatchRemoveCaret', + 'PregMatchRemoveDollar', + 'PregMatchRemoveFlags', + 'PregQuote', + 'ArrayItemRemoval', + 'CatchBlockRemoval', + 'CloneRemoval', + 'ConcatOperandRemoval', + 'FunctionCallRemoval', + 'MatchArmRemoval', + 'MethodCallRemoval', + 'SharedCaseRemoval', + 'ArrayOneItem', + 'FloatNegation', + 'FunctionCall', + 'IntegerNegation', + 'NewObject', + 'This', + 'YieldValue', + 'Spaceship', + 'DoWhile', + 'Foreach_', + 'For_', + 'While_', + 'CastArray', + 'CastBool', + 'CastFloat', + 'CastInt', + 'CastObject', + 'CastString', + 'UnwrapArrayChangeKeyCase', + 'UnwrapArrayChunk', + 'UnwrapArrayColumn', + 'UnwrapArrayCombine', + 'UnwrapArrayDiff', + 'UnwrapArrayDiffAssoc', + 'UnwrapArrayDiffKey', + 'UnwrapArrayDiffUassoc', + 'UnwrapArrayDiffUkey', + 'UnwrapArrayFilter', + 'UnwrapArrayFlip', + 'UnwrapArrayIntersect', + 'UnwrapArrayIntersectAssoc', + 'UnwrapArrayIntersectKey', + 'UnwrapArrayIntersectUassoc', + 'UnwrapArrayIntersectUkey', + 'UnwrapArrayKeys', + 'UnwrapArrayMerge', + 'UnwrapArrayMergeRecursive', + 'UnwrapArrayPad', + 'UnwrapArrayReduce', + 'UnwrapArrayReplace', + 'UnwrapArrayReplaceRecursive', + 'UnwrapArrayReverse', + 'UnwrapArraySlice', + 'UnwrapArraySplice', + 'UnwrapArrayUdiff', + 'UnwrapArrayUdiffAssoc', + 'UnwrapArrayUdiffUassoc', + 'UnwrapArrayUintersect', + 'UnwrapArrayUintersectAssoc', + 'UnwrapArrayUintersectUassoc', + 'UnwrapArrayUnique', + 'UnwrapArrayValues', + 'UnwrapLcFirst', + 'UnwrapLtrim', + 'UnwrapRtrim', + 'UnwrapStrIreplace', + 'UnwrapStrRepeat', + 'UnwrapStrReplace', + 'UnwrapStrRev', + 'UnwrapStrShuffle', + 'UnwrapStrToLower', + 'UnwrapStrToUpper', + 'UnwrapSubstr', + 'UnwrapTrim', + 'UnwrapUcFirst', + 'UnwrapUcWords', + 'UnwrapFinally', + 'BCMath', + 'MBString', + 'SyntaxError', + ]; + #[DataProvider('valuesProvider')] public function test_it_can_be_instantiated( string $description, @@ -75,4 +245,40 @@ public static function valuesProvider(): iterable 'The diff', ]; } + + #[DataProvider('mutatorsProvider')] + public function test_it_must_define_remedies(Mutator $mutator): void + { + $this->assertNotNull( + $mutator::getDefinition()->getRemedies(), + sprintf( + 'Definition of [%s] must provide remedies.', + $mutator->getName(), + ), + ); + } + + public static function mutatorsProvider(): iterable + { + $mutatorFactory = SingletonContainer::getContainer()->getMutatorFactory(); + + $mutators = $mutatorFactory->create( + array_fill_keys( + ProfileList::ALL_MUTATORS, + [], + ), + false, + ); + + $checkedMutators = array_diff_key( + $mutators, + array_flip(self::MUTATORS_WITHOUT_REMEDIES), + ); + + foreach ($checkedMutators as $name => $mutator) { + self::assertInstanceOf(Mutator::class, $mutator); + + yield $name => [$mutator]; + } + } }