diff --git a/app/infection-builds/infection.phar b/app/infection-builds/infection.phar index a2548e1..cbff5de 100755 Binary files a/app/infection-builds/infection.phar and b/app/infection-builds/infection.phar differ diff --git a/app/src/Infection/ConfigBuilder.php b/app/src/Infection/ConfigBuilder.php index 3b331cd..7c571c9 100644 --- a/app/src/Infection/ConfigBuilder.php +++ b/app/src/Infection/ConfigBuilder.php @@ -38,6 +38,10 @@ use function json_decode; use function json_encode; use const JSON_THROW_ON_ERROR; +use function sprintf; +use function str_starts_with; +use function strlen; +use function substr; /** * This class assumes that json was already validated by Infection's JSON schema. @@ -56,10 +60,60 @@ final class ConfigBuilder private const TIMEOUT = 3; + private const NAMESPACE = 'Infected'; + public function build(string $userProvidedConfig): string { + $basicConfig = self::NOT_OVERRIDABLE_KEYS; + /** @var array{mutators?: array} $decodedOriginalConfig */ $decodedOriginalConfig = json_decode($userProvidedConfig, true, JSON_THROW_ON_ERROR); - return (string) json_encode(self::NOT_OVERRIDABLE_KEYS + $decodedOriginalConfig); + if ($this->containsCustomMutator($decodedOriginalConfig['mutators'] ?? [])) { + $basicConfig['source']['excludes'] = $this->findCustomMutators($decodedOriginalConfig['mutators'] ?? []); + } + + return (string) json_encode($basicConfig + $decodedOriginalConfig); + } + + /** + * @param array $mutators + */ + private function containsCustomMutator(array $mutators): bool + { + foreach ($mutators as $mutatorName => $mutatorConfig) { + if ($this->isCustomMutatorName($mutatorName)) { + return true; + } + } + + return false; + } + + /** + * @param array $mutators + * + * @return list + */ + private function findCustomMutators(array $mutators): array + { + $customMutatorNames = []; + + foreach ($mutators as $mutatorName => $mutatorConfig) { + if ($this->isCustomMutatorName($mutatorName)) { + $customMutatorNames[] = substr($mutatorName, strlen($this->getStaticNamespace())); + } + } + + return $customMutatorNames; + } + + private function getStaticNamespace(): string + { + return sprintf('%s\\', self::NAMESPACE); + } + + private function isCustomMutatorName(string $mutatorName): bool + { + return str_starts_with($mutatorName, $this->getStaticNamespace()); } } diff --git a/app/src/Infection/Runner.php b/app/src/Infection/Runner.php index 746b5ef..83e34bc 100644 --- a/app/src/Infection/Runner.php +++ b/app/src/Infection/Runner.php @@ -45,7 +45,7 @@ class Runner { - public const CURRENT_INFECTION_VERSION = '0.27.10'; + public const CURRENT_INFECTION_VERSION = '0.29.1'; public const CURRENT_PHPUNIT_VERSION = '10.5.10'; public const CURRENT_PHP_VERSION = '8.2.13'; diff --git a/app/src/Json/infection-config-schema.json b/app/src/Json/infection-config-schema.json index 104d60e..17f5a54 100644 --- a/app/src/Json/infection-config-schema.json +++ b/app/src/Json/infection-config-schema.json @@ -22,6 +22,9 @@ "mutators": { "type": "object", "additionalProperties": false, + "patternProperties": { + "^[\\w]+(?:\\\\[\\w]+){1,}$": { "$ref": "#/definitions/default-mutator-config" } + }, "description": "Contains the settings for different mutations and profiles", "properties": { "global-ignore": { diff --git a/app/tests/Unit/Infection/ConfigBuilderTest.php b/app/tests/Unit/Infection/ConfigBuilderTest.php index f2af767..cf897f8 100644 --- a/app/tests/Unit/Infection/ConfigBuilderTest.php +++ b/app/tests/Unit/Infection/ConfigBuilderTest.php @@ -164,5 +164,33 @@ public static function provideConfigs(): Generator ] ), ]; + + yield 'Custom mutators are automatically exluded from mutation' => [ + <<<'JSON' +{ + "mutators": {"@default": false, "Infected\\CustomMutator1": true, "Infected\\CustomMutator2": true} +} +JSON + , + [ + 'bootstrap' => './autoload.php', + 'timeout' => 3, + 'source' => [ + 'directories' => ['src'], + 'excludes' => [ + 'CustomMutator1', + 'CustomMutator2', + ], + ], + 'phpUnit' => ['customPath' => '../phpunit.phar'], + 'tmpDir' => '.', + 'logs' => ['json' => 'infection.log.json'], + 'mutators' => [ + '@default' => false, + 'Infected\\CustomMutator1' => true, + 'Infected\\CustomMutator2' => true, + ], + ], + ]; } }