diff --git a/doc/nomenclature.md b/doc/nomenclature.md index 752d8720f..205656090 100644 --- a/doc/nomenclature.md +++ b/doc/nomenclature.md @@ -2,9 +2,58 @@ ## Table of Contents +- [A](#a) + - [AST][ast] +- [M](#m) + - [Mutagenesis][mutagenesis] + - [Mutant][mutant] + - [Mutation][mutation] + - [Mutator][mutator] +- [S](#s) + - [Subject][subject] - [T](#t) - - [Tracer][tracer] - - [Trace][trace] + - [Tracer][tracer] + - [Trace][trace] + + +## A + +### AST + +Acronym for [Abstract Syntax Tree][ast-definition]. It is a tree representation of the abstract +syntactic structure of code. It is what Infection parses the code into in order to operate on it. + + +## M + +### Mutagenesis + +Process of creating a mutant from the original program. + + +### Mutant + +New program that differs from the original by applying a mutation. + + +### Mutation + +The result of applying a mutator to the AST of a subject and represents a change to be applied. + + +### Mutator + +Define a possible transformation, which applied to the AST of a subject will result in a mutation. + +In the Mutation Testing literature, mutators are also known as "mutant operator", +"mutagenic operator", "mutagen" and "mutation rule". + + +## S + +### Subject + +An addressable piece of code to be targeted for mutation testing. ## T @@ -23,5 +72,12 @@ Artifact produced by a tracer: provides the piece of source code and its associa
+[ast]: #ast +[ast-definition]: https://en.wikipedia.org/wiki/Abstract_syntax_tree +[mutagenesis]: #mutagenesis +[mutant]: #mutant +[mutation]: #mutation +[mutator]: #mutator +[subject]: #subject [tracer]: #tracer [trace]: #trace diff --git a/src/Container.php b/src/Container.php index e36d923a0..3389b345a 100644 --- a/src/Container.php +++ b/src/Container.php @@ -64,7 +64,7 @@ use Infection\Metrics\MinMsiChecker; use Infection\Mutant\MutantCodeFactory; use Infection\Mutant\MutantExecutionResultFactory; -use Infection\Mutant\MutantFactory; +use Infection\Mutant\MutationFactory; use Infection\Mutation\FileMutationGenerator; use Infection\Mutation\MutationAttributeKeys; use Infection\Mutation\MutationGenerator; @@ -225,8 +225,8 @@ public static function create(): self MutantCodeFactory::class => static function (self $container): MutantCodeFactory { return new MutantCodeFactory($container->getPrinter()); }, - MutantFactory::class => static function (self $container): MutantFactory { - return new MutantFactory( + MutationFactory::class => static function (self $container): MutationFactory { + return new MutationFactory( $container->getConfiguration()->getTmpDir(), $container->getDiffer(), $container->getPrinter(), @@ -399,7 +399,8 @@ public static function create(): self return new FileMutationGenerator( $container->getFileParser(), $container->getNodeTraverserFactory(), - $container->getLineRangeCalculator() + $container->getLineRangeCalculator(), + $container->getMutationFactory() ); }, LoggerFactory::class => static function (self $container): LoggerFactory { @@ -454,7 +455,6 @@ public static function create(): self MutationTestingRunner::class => static function (self $container): MutationTestingRunner { return new MutationTestingRunner( $container->getMutantProcessFactory(), - $container->getMutantFactory(), $container->getProcessRunner(), $container->getEventDispatcher(), $container->getConfiguration()->isDryRun() @@ -655,9 +655,9 @@ public function getMutantCodeFactory(): MutantCodeFactory return $this->get(MutantCodeFactory::class); } - public function getMutantFactory(): MutantFactory + public function getMutationFactory(): MutationFactory { - return $this->get(MutantFactory::class); + return $this->get(MutationFactory::class); } public function getDiffer(): Differ diff --git a/src/Engine.php b/src/Engine.php index c2fb03a53..532692374 100644 --- a/src/Engine.php +++ b/src/Engine.php @@ -157,12 +157,15 @@ private function runMutationAnalysis(): void : [] ); - $actualExtraOptions = $this->config->getTestFrameworkExtraOptions(); + $extraOptions = $this->config->getTestFrameworkExtraOptions(); - $filteredExtraOptionsForMutant = $this->adapter instanceof ProvidesInitialRunOnlyOptions - ? $this->testFrameworkExtraOptionsFilter->filterForMutantProcess($actualExtraOptions, $this->adapter->getInitialRunOnlyOptions()) - : $actualExtraOptions; + $mutationExtraOptions = $this->adapter instanceof ProvidesInitialRunOnlyOptions + ? $this->testFrameworkExtraOptionsFilter->filterForMutantProcess( + $extraOptions, + $this->adapter->getInitialRunOnlyOptions() + ) + : $extraOptions; - $this->mutationTestingRunner->run($mutations, $filteredExtraOptionsForMutant); + $this->mutationTestingRunner->run($mutations, $mutationExtraOptions); } } diff --git a/src/Event/Subscriber/MutationTestingConsoleLoggerSubscriber.php b/src/Event/Subscriber/MutationTestingConsoleLoggerSubscriber.php index 57c835297..ab02f56d9 100644 --- a/src/Event/Subscriber/MutationTestingConsoleLoggerSubscriber.php +++ b/src/Event/Subscriber/MutationTestingConsoleLoggerSubscriber.php @@ -138,7 +138,7 @@ private function showMutations(array $executionResults, string $headlinePrefix): ), ]); - $this->output->writeln($this->diffColorizer->colorize($executionResult->getMutantDiff())); + $this->output->writeln($this->diffColorizer->colorize($executionResult->getMutationDiff())); } } diff --git a/src/Logger/TextFileLogger.php b/src/Logger/TextFileLogger.php index 03d8ee62b..5ae03179d 100644 --- a/src/Logger/TextFileLogger.php +++ b/src/Logger/TextFileLogger.php @@ -138,7 +138,7 @@ private function getResultsLine( $lines[] = self::getMutatorLine($index, $executionResult); $lines[] = ''; - $lines[] = Str::trimLineReturns($executionResult->getMutantDiff()); + $lines[] = Str::trimLineReturns($executionResult->getMutationDiff()); if ($this->debugMode) { $lines[] = ''; diff --git a/src/Mutant/Mutant.php b/src/Mutant/Mutant.php deleted file mode 100644 index ca0562fb5..000000000 --- a/src/Mutant/Mutant.php +++ /dev/null @@ -1,96 +0,0 @@ -mutantFilePath = $mutantFilePath; - $this->mutation = $mutation; - $this->mutatedCode = $mutatedCode; - $this->diff = $diff; - } - - public function getFilePath(): string - { - return $this->mutantFilePath; - } - - public function getMutation(): Mutation - { - return $this->mutation; - } - - public function getMutatedCode(): string - { - return $this->mutatedCode; - } - - public function getDiff(): string - { - return $this->diff; - } - - public function isCoveredByTest(): bool - { - return $this->mutation->isCoveredByTest(); - } - - /** - * @return TestLocation[] - */ - public function getTests(): array - { - return $this->mutation->getAllTests(); - } -} diff --git a/src/Mutant/MutantCodeFactory.php b/src/Mutant/MutantCodeFactory.php index ac0035b66..9ec08113b 100644 --- a/src/Mutant/MutantCodeFactory.php +++ b/src/Mutant/MutantCodeFactory.php @@ -35,9 +35,10 @@ namespace Infection\Mutant; -use Infection\Mutation\Mutation; +use Infection\PhpParser\MutatedNode; use Infection\PhpParser\Visitor\CloneVisitor; use Infection\PhpParser\Visitor\MutatorVisitor; +use PhpParser\Node; use PhpParser\NodeTraverser; use PhpParser\PrettyPrinterAbstract; @@ -54,14 +55,23 @@ public function __construct(PrettyPrinterAbstract $prettyPrinter) $this->printer = $prettyPrinter; } - public function createCode(Mutation $mutation): string - { + /** + * @param array $attributes + * @param Node[] $originalFileAst + * @param class-string $mutatedNodeClass + */ + public function createCode( + array $attributes, + array $originalFileAst, + string $mutatedNodeClass, + MutatedNode $mutatedNode + ): string { $traverser = new NodeTraverser(); $traverser->addVisitor(new CloneVisitor()); - $traverser->addVisitor(new MutatorVisitor($mutation)); + $traverser->addVisitor(new MutatorVisitor($attributes, $mutatedNodeClass, $mutatedNode)); - $mutatedStatements = $traverser->traverse($mutation->getOriginalFileAst()); + $mutatedStatements = $traverser->traverse($originalFileAst); return $this->printer->prettyPrintFile($mutatedStatements); } diff --git a/src/Mutant/MutantExecutionResult.php b/src/Mutant/MutantExecutionResult.php index 8f36841aa..05e4a5b61 100644 --- a/src/Mutant/MutantExecutionResult.php +++ b/src/Mutant/MutantExecutionResult.php @@ -36,6 +36,7 @@ namespace Infection\Mutant; use function array_keys; +use Infection\Mutation\Mutation; use Infection\Mutator\ProfileList; use Webmozart\Assert\Assert; @@ -48,7 +49,7 @@ class MutantExecutionResult private $processCommandLine; private $processOutput; private $detectionStatus; - private $mutantDiff; + private $mutationDiff; private $mutatorName; private $originalFilePath; private $originalStartingLine; @@ -57,7 +58,7 @@ public function __construct( string $processCommandLine, string $processOutput, string $detectionStatus, - string $mutantDiff, + string $mutationDiff, string $mutatorName, string $originalFilePath, int $originalStartingLine @@ -68,22 +69,20 @@ public function __construct( $this->processCommandLine = $processCommandLine; $this->processOutput = $processOutput; $this->detectionStatus = $detectionStatus; - $this->mutantDiff = $mutantDiff; + $this->mutationDiff = $mutationDiff; $this->mutatorName = $mutatorName; $this->originalFilePath = $originalFilePath; $this->originalStartingLine = $originalStartingLine; } - public static function createFromNonCoveredMutant(Mutant $mutant): self + public static function createFromNonCoveredByTestsMutation(Mutation $mutation): self { - $mutation = $mutant->getMutation(); - return new self( '', '', DetectionStatus::NOT_COVERED, - $mutant->getDiff(), - $mutant->getMutation()->getMutatorName(), + $mutation->getDiff(), + $mutation->getMutatorName(), $mutation->getOriginalFilePath(), $mutation->getOriginalStartingLine() ); @@ -104,9 +103,9 @@ public function getDetectionStatus(): string return $this->detectionStatus; } - public function getMutantDiff(): string + public function getMutationDiff(): string { - return $this->mutantDiff; + return $this->mutationDiff; } public function getMutatorName(): string diff --git a/src/Mutant/MutantExecutionResultFactory.php b/src/Mutant/MutantExecutionResultFactory.php index 53a9dd7d5..8198485a5 100644 --- a/src/Mutant/MutantExecutionResultFactory.php +++ b/src/Mutant/MutantExecutionResultFactory.php @@ -57,14 +57,13 @@ public function __construct(TestFrameworkAdapter $testFrameworkAdapter) public function createFromProcess(MutantProcess $mutantProcess): MutantExecutionResult { $process = $mutantProcess->getProcess(); - $mutant = $mutantProcess->getMutant(); - $mutation = $mutant->getMutation(); + $mutation = $mutantProcess->getMutation(); return new MutantExecutionResult( $process->getCommandLine(), $this->retrieveProcessOutput($process), $this->retrieveDetectionStatus($mutantProcess), - $mutant->getDiff(), + $mutation->getDiff(), $mutation->getMutatorName(), $mutation->getOriginalFilePath(), $mutation->getOriginalStartingLine() @@ -86,7 +85,7 @@ private function retrieveProcessOutput(Process $process): string private function retrieveDetectionStatus(MutantProcess $mutantProcess): string { - if (!$mutantProcess->getMutant()->isCoveredByTest()) { + if (!$mutantProcess->getMutation()->isCoveredByTest()) { return DetectionStatus::NOT_COVERED; } diff --git a/src/Mutant/MutantFactory.php b/src/Mutant/MutationFactory.php similarity index 52% rename from src/Mutant/MutantFactory.php rename to src/Mutant/MutationFactory.php index 4614467ff..c9f47b508 100644 --- a/src/Mutant/MutantFactory.php +++ b/src/Mutant/MutationFactory.php @@ -35,17 +35,25 @@ namespace Infection\Mutant; +use function array_intersect_key; +use function implode; +use Infection\AbstractTestFramework\Coverage\TestLocation; use Infection\Differ\Differ; use Infection\Mutation\Mutation; +use Infection\Mutation\MutationAttributeKeys; +use Infection\PhpParser\MutatedNode; +use function md5; use PhpParser\Node; use PhpParser\PrettyPrinterAbstract; +use function Safe\array_flip; use function Safe\sprintf; +use Webmozart\Assert\Assert; /** * @internal * @final */ -class MutantFactory +class MutationFactory { private $tmpDir; private $differ; @@ -55,7 +63,7 @@ class MutantFactory * @var string[] */ private $printedFileCache = []; - private $mutantCodeFactory; + private $codeFactory; public function __construct( string $tmpDir, @@ -66,35 +74,103 @@ public function __construct( $this->tmpDir = $tmpDir; $this->differ = $differ; $this->printer = $printer; - $this->mutantCodeFactory = $mutantCodeFactory; + $this->codeFactory = $mutantCodeFactory; } - public function create(Mutation $mutation): Mutant - { - $mutantFilePath = sprintf( - '%s/mutant.%s.infection.php', + /** + * @param Node[] $originalFileAst + * @param array $attributes + * @param class-string $mutatedNodeClass + * @param TestLocation[] $tests + */ + public function create( + string $originalFilePath, + array $originalFileAst, + string $mutatorName, + array $attributes, + string $mutatedNodeClass, + MutatedNode $mutatedNode, + int $mutationByMutatorIndex, + array $tests + ): Mutation { + foreach (MutationAttributeKeys::ALL as $key) { + Assert::keyExists($attributes, $key); + } + + $attributes = array_intersect_key($attributes, array_flip(MutationAttributeKeys::ALL)); + + $hash = self::createHash( + $originalFilePath, + $mutatorName, + $attributes, + $mutationByMutatorIndex + ); + + $mutationFilePath = sprintf( + '%s/mutation.%s.infection.php', $this->tmpDir, - $mutation->getHash() + $hash ); - $mutatedCode = $this->mutantCodeFactory->createCode($mutation); + $mutatedCode = $this->codeFactory->createCode( + $attributes, + $originalFileAst, + $mutatedNodeClass, + $mutatedNode + ); - return new Mutant( - $mutantFilePath, - $mutation, + return new Mutation( + $originalFilePath, + $mutatorName, + (int) $attributes[MutationAttributeKeys::START_LINE], + $tests, + $hash, + $mutationFilePath, $mutatedCode, - $this->createMutantDiff($mutation, $mutatedCode) + $this->createMutationDiff( + $originalFilePath, + $originalFileAst, + $mutatedCode + ) ); } - private function createMutantDiff(Mutation $mutation, string $mutantCode): string - { + /** + * @param array $attributes + */ + private static function createHash( + string $originalFilePath, + string $mutatorName, + array $attributes, + int $mutationByMutatorIndex + ): string { + $hashKeys = [ + $originalFilePath, + $mutatorName, + $mutationByMutatorIndex, + ]; + + foreach ($attributes as $attribute) { + $hashKeys[] = $attribute; + } + + return md5(implode('_', $hashKeys)); + } + + /** + * @param Node[] $originalFileAst + */ + private function createMutationDiff( + string $originalFilePath, + array $originalFileAst, + string $mutationCode + ): string { $originalPrettyPrintedFile = $this->getOriginalPrettyPrintedFile( - $mutation->getOriginalFilePath(), - $mutation->getOriginalFileAst() + $originalFilePath, + $originalFileAst ); - return $this->differ->diff($originalPrettyPrintedFile, $mutantCode); + return $this->differ->diff($originalPrettyPrintedFile, $mutationCode); } /** diff --git a/src/Mutation/FileMutationGenerator.php b/src/Mutation/FileMutationGenerator.php index aebae30fc..7cd117cc6 100644 --- a/src/Mutation/FileMutationGenerator.php +++ b/src/Mutation/FileMutationGenerator.php @@ -35,6 +35,7 @@ namespace Infection\Mutation; +use Infection\Mutant\MutationFactory; use Infection\Mutator\Mutator; use Infection\Mutator\NodeMutationGenerator; use Infection\PhpParser\FileParser; @@ -55,15 +56,18 @@ class FileMutationGenerator private $parser; private $traverserFactory; private $lineRangeCalculator; + private $mutationFactory; public function __construct( FileParser $parser, NodeTraverserFactory $traverserFactory, - LineRangeCalculator $lineRangeCalculator + LineRangeCalculator $lineRangeCalculator, + MutationFactory $mutationFactory ) { $this->parser = $parser; $this->traverserFactory = $traverserFactory; $this->lineRangeCalculator = $lineRangeCalculator; + $this->mutationFactory = $mutationFactory; } /** @@ -98,7 +102,8 @@ public function generate( $initialStatements, $trace, $onlyCovered, - $this->lineRangeCalculator + $this->lineRangeCalculator, + $this->mutationFactory ) ); diff --git a/src/Mutation/Mutation.php b/src/Mutation/Mutation.php index 2e68913bd..5d1a93110 100644 --- a/src/Mutation/Mutation.php +++ b/src/Mutation/Mutation.php @@ -35,16 +35,10 @@ namespace Infection\Mutation; -use function array_intersect_key; use function array_keys; use function count; -use function implode; use Infection\AbstractTestFramework\Coverage\TestLocation; use Infection\Mutator\ProfileList; -use Infection\PhpParser\MutatedNode; -use function md5; -use PhpParser\Node; -use function Safe\array_flip; use Webmozart\Assert\Assert; /** @@ -55,49 +49,38 @@ class Mutation { private $originalFilePath; private $mutatorName; - private $mutatedNodeClass; - private $mutatedNode; - private $mutationByMutatorIndex; - private $attributes; - private $originalFileAst; + private $originalStartingLine; private $tests; private $coveredByTests; + private $mutationHash; + private $mutationFilePath; + private $mutatedCode; + private $diff; /** - * @var string|null - */ - private $hash; - - /** - * @param Node[] $originalFileAst - * @param array $attributes * @param TestLocation[] $tests */ public function __construct( string $originalFilePath, - array $originalFileAst, string $mutatorName, - array $attributes, - string $mutatedNodeClass, - MutatedNode $mutatedNode, - int $mutationByMutatorIndex, - array $tests + int $originalStartingLine, + array $tests, + string $mutationHash, + string $mutationFilePath, + string $mutatedCode, + string $diff ) { Assert::oneOf($mutatorName, array_keys(ProfileList::ALL_MUTATORS)); - foreach (MutationAttributeKeys::ALL as $key) { - Assert::keyExists($attributes, $key); - } - $this->originalFilePath = $originalFilePath; - $this->originalFileAst = $originalFileAst; $this->mutatorName = $mutatorName; - $this->attributes = array_intersect_key($attributes, array_flip(MutationAttributeKeys::ALL)); - $this->mutatedNodeClass = $mutatedNodeClass; - $this->mutatedNode = $mutatedNode; - $this->mutationByMutatorIndex = $mutationByMutatorIndex; + $this->originalStartingLine = $originalStartingLine; $this->tests = $tests; $this->coveredByTests = count($tests) > 0; + $this->mutationHash = $mutationHash; + $this->mutationFilePath = $mutationFilePath; + $this->mutatedCode = $mutatedCode; + $this->diff = $diff; } public function getOriginalFilePath(): string @@ -105,43 +88,16 @@ public function getOriginalFilePath(): string return $this->originalFilePath; } - /** - * @return Node[] - */ - public function getOriginalFileAst(): array - { - return $this->originalFileAst; - } - public function getMutatorName(): string { return $this->mutatorName; } - /** - * @return (string|int|float)[] - */ - public function getAttributes(): array - { - return $this->attributes; - } - public function getOriginalStartingLine(): int { - return (int) $this->attributes['startLine']; - } - - public function getMutatedNodeClass(): string - { - return $this->mutatedNodeClass; - } - - public function getMutatedNode(): MutatedNode - { - return $this->mutatedNode; + return $this->originalStartingLine; } - // TODO: hasTest()? public function isCoveredByTest(): bool { return $this->coveredByTests; @@ -157,21 +113,21 @@ public function getAllTests(): array public function getHash(): string { - return $this->hash ?? $this->hash = $this->createHash(); + return $this->mutationHash; } - private function createHash(): string + public function getFilePath(): string { - $hashKeys = [ - $this->originalFilePath, - $this->mutatorName, - $this->mutationByMutatorIndex, - ]; + return $this->mutationFilePath; + } - foreach ($this->attributes as $attribute) { - $hashKeys[] = $attribute; - } + public function getMutatedCode(): string + { + return $this->mutatedCode; + } - return md5(implode('_', $hashKeys)); + public function getDiff(): string + { + return $this->diff; } } diff --git a/src/Mutator/NodeMutationGenerator.php b/src/Mutator/NodeMutationGenerator.php index d624425bd..c191d7df6 100644 --- a/src/Mutator/NodeMutationGenerator.php +++ b/src/Mutator/NodeMutationGenerator.php @@ -37,6 +37,7 @@ use function count; use function get_class; +use Infection\Mutant\MutationFactory; use Infection\Mutation\Mutation; use Infection\PhpParser\MutatedNode; use Infection\PhpParser\Visitor\ReflectionVisitor; @@ -61,6 +62,7 @@ class NodeMutationGenerator private $trace; private $onlyCovered; private $lineRangeCalculator; + private $mutationFactory; /** * @param Mutator[] $mutators @@ -72,7 +74,8 @@ public function __construct( array $fileNodes, Trace $trace, bool $onlyCovered, - LineRangeCalculator $lineRangeCalculator + LineRangeCalculator $lineRangeCalculator, + MutationFactory $mutationFactory ) { Assert::allIsInstanceOf($mutators, Mutator::class); @@ -82,6 +85,7 @@ public function __construct( $this->trace = $trace; $this->onlyCovered = $onlyCovered; $this->lineRangeCalculator = $lineRangeCalculator; + $this->mutationFactory = $mutationFactory; } /** @@ -135,7 +139,7 @@ private function generateForMutator(Node $node, Mutator $mutator): iterable $mutationByMutatorIndex = 0; foreach ($mutator->mutate($node) as $mutatedNode) { - yield new Mutation( + yield $this->mutationFactory->create( $this->filePath, $this->fileNodes, $mutator->getName(), diff --git a/src/PhpParser/Visitor/MutatorVisitor.php b/src/PhpParser/Visitor/MutatorVisitor.php index 411225d4d..01732054f 100644 --- a/src/PhpParser/Visitor/MutatorVisitor.php +++ b/src/PhpParser/Visitor/MutatorVisitor.php @@ -38,6 +38,7 @@ use function array_key_exists; use function get_class; use Infection\Mutation\Mutation; +use Infection\PhpParser\MutatedNode; use PhpParser\Node; use PhpParser\NodeVisitorAbstract; @@ -46,11 +47,22 @@ */ final class MutatorVisitor extends NodeVisitorAbstract { - private $mutation; + private $attributes; + private $mutatedNodeClass; + private $mutatedNode; - public function __construct(Mutation $mutation) - { - $this->mutation = $mutation; + /** + * @param array $attributes + * @param class-string $mutatedNodeClass + */ + public function __construct( + array $attributes, + string $mutatedNodeClass, + MutatedNode $mutatedNode + ) { + $this->attributes = $attributes; + $this->mutatedNodeClass = $mutatedNodeClass; + $this->mutatedNode = $mutatedNode; } public function leaveNode(Node $node) @@ -61,13 +73,13 @@ public function leaveNode(Node $node) return null; } - $mutatedAttributes = $this->mutation->getAttributes(); + $mutatedAttributes = $this->attributes; $samePosition = $attributes['startTokenPos'] === $mutatedAttributes['startTokenPos'] && $attributes['endTokenPos'] === $mutatedAttributes['endTokenPos']; - if ($samePosition && $this->mutation->getMutatedNodeClass() === get_class($node)) { - return $this->mutation->getMutatedNode()->unwrap(); + if ($samePosition && $this->mutatedNodeClass === get_class($node)) { + return $this->mutatedNode->unwrap(); // TODO STOP TRAVERSING // TODO check all built-in visitors, in particular FirstFindingVisitor // TODO beforeTraverse - FirstFindingVisitor diff --git a/src/Process/Factory/MutantProcessFactory.php b/src/Process/Factory/MutantProcessFactory.php index 27081a8e0..469791c67 100644 --- a/src/Process/Factory/MutantProcessFactory.php +++ b/src/Process/Factory/MutantProcessFactory.php @@ -38,8 +38,8 @@ use Infection\AbstractTestFramework\TestFrameworkAdapter; use Infection\Event\EventDispatcher\EventDispatcher; use Infection\Event\MutantProcessWasFinished; -use Infection\Mutant\Mutant; use Infection\Mutant\MutantExecutionResultFactory; +use Infection\Mutation\Mutation; use Infection\Process\MutantProcess; use function method_exists; use Symfony\Component\Process\Process; @@ -68,14 +68,14 @@ public function __construct( $this->resultFactory = $resultFactory; } - public function createProcessForMutant(Mutant $mutant, string $testFrameworkExtraOptions = ''): MutantProcess + public function createProcessForMutation(Mutation $mutation, string $testFrameworkExtraOptions = ''): MutantProcess { $process = new Process( $this->testFrameworkAdapter->getMutantCommandLine( - $mutant->getTests(), - $mutant->getFilePath(), - $mutant->getMutation()->getHash(), - $mutant->getMutation()->getOriginalFilePath(), + $mutation->getAllTests(), + $mutation->getFilePath(), + $mutation->getHash(), + $mutation->getOriginalFilePath(), $testFrameworkExtraOptions ) ); @@ -87,7 +87,7 @@ public function createProcessForMutant(Mutant $mutant, string $testFrameworkExtr $process->inheritEnvironmentVariables(); } - $mutantProcess = new MutantProcess($process, $mutant); + $mutantProcess = new MutantProcess($process, $mutation); $eventDispatcher = $this->eventDispatcher; $resultFactory = $this->resultFactory; diff --git a/src/Process/MutantProcess.php b/src/Process/MutantProcess.php index d6bbde333..a01b2f994 100644 --- a/src/Process/MutantProcess.php +++ b/src/Process/MutantProcess.php @@ -36,7 +36,7 @@ namespace Infection\Process; use Closure; -use Infection\Mutant\Mutant; +use Infection\Mutation\Mutation; use Infection\Process\Runner\ProcessBearer; use Symfony\Component\Process\Process; @@ -47,7 +47,7 @@ class MutantProcess implements ProcessBearer { private $process; - private $mutant; + private $mutation; private $callback; /** @@ -55,10 +55,10 @@ class MutantProcess implements ProcessBearer */ private $timedOut = false; - public function __construct(Process $process, Mutant $mutant) + public function __construct(Process $process, Mutation $mutation) { $this->process = $process; - $this->mutant = $mutant; + $this->mutation = $mutation; $this->callback = static function (): void {}; } @@ -67,9 +67,9 @@ public function getProcess(): Process return $this->process; } - public function getMutant(): Mutant + public function getMutation(): Mutation { - return $this->mutant; + return $this->mutation; } public function markAsTimedOut(): void diff --git a/src/Process/Runner/MutationTestingRunner.php b/src/Process/Runner/MutationTestingRunner.php index 7e07bac00..6458641e7 100644 --- a/src/Process/Runner/MutationTestingRunner.php +++ b/src/Process/Runner/MutationTestingRunner.php @@ -40,9 +40,7 @@ use Infection\Event\MutationTestingWasFinished; use Infection\Event\MutationTestingWasStarted; use Infection\IterableCounter; -use Infection\Mutant\Mutant; use Infection\Mutant\MutantExecutionResult; -use Infection\Mutant\MutantFactory; use Infection\Mutation\Mutation; use Infection\Process\Factory\MutantProcessFactory; use function Pipeline\take; @@ -54,7 +52,6 @@ final class MutationTestingRunner { private $processFactory; - private $mutantFactory; private $processRunner; private $eventDispatcher; private $fileSystem; @@ -62,14 +59,12 @@ final class MutationTestingRunner public function __construct( MutantProcessFactory $processFactory, - MutantFactory $mutantFactory, ProcessRunner $processRunner, EventDispatcher $eventDispatcher, Filesystem $fileSystem, bool $runConcurrently ) { $this->processFactory = $processFactory; - $this->mutantFactory = $mutantFactory; $this->processRunner = $processRunner; $this->eventDispatcher = $eventDispatcher; $this->fileSystem = $fileSystem; @@ -81,29 +76,28 @@ public function __construct( */ public function run(iterable $mutations, string $testFrameworkExtraOptions): void { - $numberOfMutants = IterableCounter::bufferAndCountIfNeeded($mutations, $this->runConcurrently); - $this->eventDispatcher->dispatch(new MutationTestingWasStarted($numberOfMutants)); + $numberOfMutations = IterableCounter::bufferAndCountIfNeeded($mutations, $this->runConcurrently); + $this->eventDispatcher->dispatch(new MutationTestingWasStarted($numberOfMutations)); $processes = take($mutations) - ->map(function (Mutation $mutation): Mutant { - return $this->mutantFactory->create($mutation); - }) - ->filter(function (Mutant $mutant) { - // It's a proxy call to Mutation, can be done one stage up - if ($mutant->isCoveredByTest()) { + ->filter(function (Mutation $mutation) { + if ($mutation->isCoveredByTest()) { return true; } $this->eventDispatcher->dispatch(new MutantProcessWasFinished( - MutantExecutionResult::createFromNonCoveredMutant($mutant) + MutantExecutionResult::createFromNonCoveredByTestsMutation($mutation) )); return false; }) - ->map(function (Mutant $mutant) use ($testFrameworkExtraOptions): ProcessBearer { - $this->fileSystem->dumpFile($mutant->getFilePath(), $mutant->getMutatedCode()); + ->map(function (Mutation $mutation) use ($testFrameworkExtraOptions): ProcessBearer { + $this->fileSystem->dumpFile($mutation->getFilePath(), $mutation->getMutatedCode()); - $process = $this->processFactory->createProcessForMutant($mutant, $testFrameworkExtraOptions); + $process = $this->processFactory->createProcessForMutation( + $mutation, + $testFrameworkExtraOptions + ); return $process; }) diff --git a/src/TestFramework/AbstractTestFrameworkAdapter.php b/src/TestFramework/AbstractTestFrameworkAdapter.php index 599196ca9..09cf0ba05 100644 --- a/src/TestFramework/AbstractTestFrameworkAdapter.php +++ b/src/TestFramework/AbstractTestFrameworkAdapter.php @@ -98,7 +98,7 @@ public function getInitialTestRunCommandLine( } /** - * Returns array of arguments to pass them into the Mutant Symfony Process + * Returns array of arguments to pass them into the Mutant Symfony's process * * @param TestLocation[] $tests * diff --git a/src/TestFramework/Config/MutationConfigBuilder.php b/src/TestFramework/Config/MutationConfigBuilder.php index 15daf7fc8..62124a302 100644 --- a/src/TestFramework/Config/MutationConfigBuilder.php +++ b/src/TestFramework/Config/MutationConfigBuilder.php @@ -58,8 +58,11 @@ abstract public function build( string $mutationOriginalFilePath ): string; - protected function getInterceptorFileContent(string $interceptorPath, string $originalFilePath, string $mutantFilePath): string - { + protected function getInterceptorFileContent( + string $interceptorPath, + string $originalFilePath, + string $mutantFilePath + ): string { $infectionPhar = ''; if (strpos(__FILE__, 'phar:') === 0) { diff --git a/src/TestFramework/PhpUnit/Config/Builder/MutationConfigBuilder.php b/src/TestFramework/PhpUnit/Config/Builder/MutationConfigBuilder.php index 063a8ba04..623b45e0e 100644 --- a/src/TestFramework/PhpUnit/Config/Builder/MutationConfigBuilder.php +++ b/src/TestFramework/PhpUnit/Config/Builder/MutationConfigBuilder.php @@ -78,7 +78,6 @@ public function __construct( ) { $this->tmpDir = $tmpDir; $this->projectDir = $projectDir; - $this->originalXmlConfigContent = $originalXmlConfigContent; $this->configManipulator = $configManipulator; $this->jUnitTestCaseSorter = $jUnitTestCaseSorter; diff --git a/src/TestFramework/TestFrameworkExtraOptionsFilter.php b/src/TestFramework/TestFrameworkExtraOptionsFilter.php index 417d5ff4e..926462b97 100644 --- a/src/TestFramework/TestFrameworkExtraOptionsFilter.php +++ b/src/TestFramework/TestFrameworkExtraOptionsFilter.php @@ -47,14 +47,17 @@ final class TestFrameworkExtraOptionsFilter { /** * @param string[] $initialRunOnlyOptions - * - * @throws \Safe\Exceptions\PcreException - * @throws \Safe\Exceptions\StringsException */ - public function filterForMutantProcess(string $actualExtraOptions, array $initialRunOnlyOptions): string - { + public function filterForMutantProcess( + string $actualExtraOptions, + array $initialRunOnlyOptions + ): string { foreach ($initialRunOnlyOptions as $initialRunOnlyOption) { - $actualExtraOptions = preg_replace(sprintf('/%s[\=| ](?:\"[^\"]*\"|\'[^\']*\'|[^\ ]*)/', $initialRunOnlyOption), '', $actualExtraOptions); + $actualExtraOptions = preg_replace( + sprintf('/%s[\=| ](?:\"[^\"]*\"|\'[^\']*\'|[^\ ]*)/', $initialRunOnlyOption), + '', + $actualExtraOptions + ); Assert::notNull($actualExtraOptions); } diff --git a/tests/phpunit/AutoReview/ContainerTest.php b/tests/phpunit/AutoReview/ContainerTest.php index fbf1569fb..949aa0c2c 100644 --- a/tests/phpunit/AutoReview/ContainerTest.php +++ b/tests/phpunit/AutoReview/ContainerTest.php @@ -98,6 +98,7 @@ static function (string $path): string { __DIR__ . '/ContainerTest.php', __DIR__ . '/../ContainerTest.php', __DIR__ . '/../SingletonContainer.php', + __DIR__ . '/../Mutation/FileMutationGeneratorTest.php', ] ); diff --git a/tests/phpunit/AutoReview/Event/SubscriberTest.php b/tests/phpunit/AutoReview/Event/SubscriberTest.php index 6ca6660f8..394659077 100644 --- a/tests/phpunit/AutoReview/Event/SubscriberTest.php +++ b/tests/phpunit/AutoReview/Event/SubscriberTest.php @@ -109,7 +109,10 @@ private function assertIsSubscriptionMethod(string $subscriberClass, ReflectionM $this->assertSame( $expectedSubscriptionMethodName, $method->getName(), - 'Expected the subscription method to follow the project naming convention' + sprintf( + 'Expected the subscription method of "%s" to follow the project naming convention', + $subscriberClass + ) ); } } diff --git a/tests/phpunit/Event/MutantProcessWasFinishedTest.php b/tests/phpunit/Event/MutantProcessWasFinishedTest.php index c773bb9ac..63dab4023 100644 --- a/tests/phpunit/Event/MutantProcessWasFinishedTest.php +++ b/tests/phpunit/Event/MutantProcessWasFinishedTest.php @@ -41,7 +41,7 @@ final class MutantProcessWasFinishedTest extends TestCase { - public function test_it_exposes_its_mutant_process(): void + public function test_it_exposes_its_execution_result(): void { $executionResultMock = $this->createMock(MutantExecutionResult::class); diff --git a/tests/phpunit/Fixtures/SimpleMutation.php b/tests/phpunit/Fixtures/SimpleMutation.php index 061461498..926c451ec 100644 --- a/tests/phpunit/Fixtures/SimpleMutation.php +++ b/tests/phpunit/Fixtures/SimpleMutation.php @@ -5,54 +5,50 @@ namespace Infection\Tests\Fixtures; use Infection\Mutation\Mutation; -use Infection\Mutator\Mutator; +use Infection\Mutation\MutationAttributeKeys; use Infection\PhpParser\MutatedNode; use PhpParser\Node; class SimpleMutation extends Mutation { - /** - * @var Mutator - */ - private $mutator; - - /** - * @var Node[] - */ private $originalFileAst; - - /** - * @var MutatedNode - */ private $mutatedNode; - /** - * @var array - */ private $attributes; - /** - * @var string - */ private $mutatedNodeClass; + /** + * @param Node[] $originalFileAst + * @param class-string $mutatorName + * @param array $attributes + * @param class-string $mutatedNodeClass + */ public function __construct( array $originalFileAst, - Mutator $mutator, - $mutatedNode, + string $mutatorName, + MutatedNode $mutatedNode, array $attributes, string $mutatedNodeClass ) { + parent::__construct( + '/path/to/Foo.php', + $mutatorName, + (int) $attributes[MutationAttributeKeys::START_LINE], + [], + 'hash', + '/path/to/MutatedFoo.php', + 'mutatedCode', + 'diff' + ); + $this->originalFileAst = $originalFileAst; - $this->mutator = $mutator; $this->mutatedNode = $mutatedNode; $this->attributes = $attributes; $this->mutatedNodeClass = $mutatedNodeClass; } - public function getMutator(): Mutator - { - return $this->mutator; - } - + /** + * @return Node[] + */ public function getOriginalFileAst(): array { return $this->originalFileAst; @@ -63,6 +59,9 @@ public function getMutatedNode(): MutatedNode return $this->mutatedNode; } + /** + * @return array + */ public function getAttributes(): array { return $this->attributes; diff --git a/tests/phpunit/Fixtures/SimpleMutationsCollectorVisitor.php b/tests/phpunit/Fixtures/SimpleMutationsCollectorVisitor.php index 33e053386..71c0937a6 100644 --- a/tests/phpunit/Fixtures/SimpleMutationsCollectorVisitor.php +++ b/tests/phpunit/Fixtures/SimpleMutationsCollectorVisitor.php @@ -14,10 +14,8 @@ */ final class SimpleMutationsCollectorVisitor extends NodeVisitorAbstract { - /** - * @var Mutator[] - */ private $mutator; + private $fileAst; /** * @var SimpleMutation[] @@ -25,10 +23,9 @@ final class SimpleMutationsCollectorVisitor extends NodeVisitorAbstract private $mutations = []; /** - * @var Node[] + * @param Mutator $mutator + * @param Node[] $fileAst */ - private $fileAst; - public function __construct(Mutator $mutator, array $fileAst) { $this->mutator = $mutator; @@ -46,7 +43,7 @@ public function leaveNode(Node $node) foreach ($this->mutator->mutate($node) as $mutatedNode) { $this->mutations[] = new SimpleMutation( $this->fileAst, - $this->mutator, + $this->mutator->getName(), MutatedNode::wrap($mutatedNode), $node->getAttributes(), get_class($node) diff --git a/tests/phpunit/Fixtures/TestFramework/DummyTestFrameworkAdapter.php b/tests/phpunit/Fixtures/TestFramework/DummyTestFrameworkAdapter.php index 7f2d982a7..fcf94fd83 100644 --- a/tests/phpunit/Fixtures/TestFramework/DummyTestFrameworkAdapter.php +++ b/tests/phpunit/Fixtures/TestFramework/DummyTestFrameworkAdapter.php @@ -23,12 +23,22 @@ public function hasJUnitReport(): bool return false; } - public function getInitialTestRunCommandLine(string $extraOptions, array $phpExtraArgs, bool $skipCoverage): array + public function getInitialTestRunCommandLine( + string $extraOptions, + array $phpExtraArgs, + bool $skipCoverage + ): array { return ['/bin/dummy']; } - public function getMutantCommandLine(array $coverageTests, string $mutatedFilePath, string $mutationHash, string $mutationOriginalFilePath, string $extraOptions): array + public function getMutantCommandLine( + array $coverageTests, + string $mutatedFilePath, + string $mutationHash, + string $mutationOriginalFilePath, + string $extraOptions + ): array { return ['/bin/dummy']; } diff --git a/tests/phpunit/Metrics/MetricsCalculatorTest.php b/tests/phpunit/Metrics/MetricsCalculatorTest.php index 3d635ebc9..d76e467dd 100644 --- a/tests/phpunit/Metrics/MetricsCalculatorTest.php +++ b/tests/phpunit/Metrics/MetricsCalculatorTest.php @@ -74,27 +74,27 @@ public function test_it_collects_all_values(): void { $calculator = new MetricsCalculator(2); - $expectedKilledResults = $this->addMutantExecutionResult( + $expectedKilledResults = $this->addMutationExecutionResult( $calculator, DetectionStatus::KILLED, 7 ); - $expectedErrorResults = $this->addMutantExecutionResult( + $expectedErrorResults = $this->addMutationExecutionResult( $calculator, DetectionStatus::ERROR, 2 ); - $expectedEscapedResults = $this->addMutantExecutionResult( + $expectedEscapedResults = $this->addMutationExecutionResult( $calculator, DetectionStatus::ESCAPED, 2 ); - $expectedTimedOutResults = $this->addMutantExecutionResult( + $expectedTimedOutResults = $this->addMutationExecutionResult( $calculator, DetectionStatus::TIMED_OUT, 2 ); - $expectedNotCoveredResults = $this->addMutantExecutionResult( + $expectedNotCoveredResults = $this->addMutationExecutionResult( $calculator, DetectionStatus::NOT_COVERED, 1 @@ -139,7 +139,7 @@ public function test_its_metrics_are_properly_updated_when_adding_a_new_process( $this->assertSame(0.0, $calculator->getCoverageRate()); $this->assertSame(0.0, $calculator->getCoveredCodeMutationScoreIndicator()); - $expectedKilledResults = $this->addMutantExecutionResult( + $expectedKilledResults = $this->addMutationExecutionResult( $calculator, DetectionStatus::KILLED, 1 @@ -156,7 +156,7 @@ public function test_its_metrics_are_properly_updated_when_adding_a_new_process( /** * @return MutantExecutionResult[] */ - private function addMutantExecutionResult( + private function addMutationExecutionResult( MetricsCalculator $calculator, string $detectionStatus, int $count diff --git a/tests/phpunit/Mutant/MutantCodeFactoryTest.php b/tests/phpunit/Mutant/MutantCodeFactoryTest.php index 450443df3..2bbf47d5b 100644 --- a/tests/phpunit/Mutant/MutantCodeFactoryTest.php +++ b/tests/phpunit/Mutant/MutantCodeFactoryTest.php @@ -36,10 +36,7 @@ namespace Infection\Tests\Mutant; use Infection\Mutant\MutantCodeFactory; -use Infection\Mutation\Mutation; -use Infection\Mutator\Arithmetic\Plus; use Infection\PhpParser\MutatedNode; -use Infection\Tests\Mutator\MutatorName; use Infection\Tests\SingletonContainer; use PhpParser\Node; use PHPUnit\Framework\TestCase; @@ -58,27 +55,51 @@ protected function setUp(): void /** * @dataProvider mutationProvider + * + * @param array $attributes + * @param Node[] $originalFileAst + * @param class-string $mutatedNodeClass */ - public function test_it_creates_the_mutant_code_from_the_given_mutation( - Mutation $mutation, - string $expectedMutantCode + public function test_it_creates_the_mutation_code_from_the_given_nodes( + array $attributes, + array $originalFileAst, + string $mutatedNodeClass, + MutatedNode $mutatedNode, + string $expectedMutationCode ): void { - $mutantCode = $this->codeFactory->createCode($mutation); + $mutationCode = $this->codeFactory->createCode( + $attributes, + $originalFileAst, + $mutatedNodeClass, + $mutatedNode + ); - $this->assertSame($expectedMutantCode, $mutantCode); + $this->assertSame($expectedMutationCode, $mutationCode); } /** * @dataProvider mutationProvider + * + * @param array $attributes + * @param Node[] $originalFileAst + * @param class-string $mutatedNodeClass */ - public function test_it_creates_the_mutant_code_without_altering_the_original_nodes( - Mutation $mutation + public function test_it_creates_the_mutation_code_without_altering_the_original_nodes( + array $attributes, + array $originalFileAst, + string $mutatedNodeClass, + MutatedNode $mutatedNode ): void { - $originalNodesDump = SingletonContainer::getNodeDumper()->dump($mutation->getOriginalFileAst()); + $originalNodesDump = SingletonContainer::getNodeDumper()->dump($originalFileAst); - $this->codeFactory->createCode($mutation); + $this->codeFactory->createCode( + $attributes, + $originalFileAst, + $mutatedNodeClass, + $mutatedNode + ); - $originalNodesDumpAfterMutation = SingletonContainer::getNodeDumper()->dump($mutation->getOriginalFileAst()); + $originalNodesDumpAfterMutation = SingletonContainer::getNodeDumper()->dump($originalFileAst); $this->assertSame($originalNodesDump, $originalNodesDumpAfterMutation); } @@ -86,79 +107,73 @@ public function test_it_creates_the_mutant_code_without_altering_the_original_no public function mutationProvider(): iterable { yield [ - new Mutation( - '/path/to/acme/Foo.php', - [new Node\Stmt\Namespace_( - new Node\Name( - 'Acme', - [ - 'startLine' => 3, - 'startTokenPos' => 4, - 'startFilePos' => 17, - 'endLine' => 3, - 'endTokenPos' => 4, - 'endFilePos' => 20, - ] - ), - [new Node\Stmt\Echo_( - [new Node\Scalar\LNumber( - 10, - [ - 'startLine' => 5, - 'startTokenPos' => 9, - 'startFilePos' => 29, - 'endLine' => 5, - 'endTokenPos' => 9, - 'endFilePos' => 30, - 'kind' => 10, - ] - )], + [ + 'startLine' => 5, + 'startTokenPos' => 9, + 'startFilePos' => 29, + 'endLine' => 5, + 'endTokenPos' => 9, + 'endFilePos' => 30, + 'kind' => 10, + ], + [new Node\Stmt\Namespace_( + new Node\Name( + 'Acme', + [ + 'startLine' => 3, + 'startTokenPos' => 4, + 'startFilePos' => 17, + 'endLine' => 3, + 'endTokenPos' => 4, + 'endFilePos' => 20, + ] + ), + [new Node\Stmt\Echo_( + [new Node\Scalar\LNumber( + 10, [ 'startLine' => 5, - 'startTokenPos' => 7, - 'startFilePos' => 24, + 'startTokenPos' => 9, + 'startFilePos' => 29, 'endLine' => 5, - 'endTokenPos' => 10, - 'endFilePos' => 31, + 'endTokenPos' => 9, + 'endFilePos' => 30, + 'kind' => 10, ] )], [ - 'startLine' => 3, - 'startTokenPos' => 2, - 'startFilePos' => 7, + 'startLine' => 5, + 'startTokenPos' => 7, + 'startFilePos' => 24, 'endLine' => 5, 'endTokenPos' => 10, 'endFilePos' => 31, - 'kind' => 1, ] )], - MutatorName::getName(Plus::class), [ - 'startLine' => 5, - 'startTokenPos' => 9, - 'startFilePos' => 29, + 'startLine' => 3, + 'startTokenPos' => 2, + 'startFilePos' => 7, 'endLine' => 5, - 'endTokenPos' => 9, - 'endFilePos' => 30, - 'kind' => 10, - ], - Node\Scalar\LNumber::class, - MutatedNode::wrap( - new Node\Scalar\LNumber( - 15, - [ - 'startLine' => 5, - 'startTokenPos' => 9, - 'startFilePos' => 29, - 'endLine' => 5, - 'endTokenPos' => 9, - 'endFilePos' => 30, - 'kind' => 10, - ] - ) - ), - 0, - [] + 'endTokenPos' => 10, + 'endFilePos' => 31, + 'kind' => 1, + ] + )], + Node\Scalar\LNumber::class, + MutatedNode::wrap( + new Node\Scalar\LNumber( + 15, + [ + 'startLine' => 5, + 'startTokenPos' => 9, + 'startFilePos' => 29, + 'endLine' => 5, + 'endTokenPos' => 9, + 'endFilePos' => 30, + 'kind' => 10, + ] + ) ), <<<'PHP' method($this->anything()) ; - $mutantProcess = new MutantProcess( - $processMock, - new Mutant( - '/path/to/mutant', - new Mutation( - $originalFilePath = 'path/to/Foo.php', - [], - $mutatorName = MutatorName::getName(For_::class), - [ - 'startLine' => $originalStartingLine = 10, - 'endLine' => 15, - 'startTokenPos' => 0, - 'endTokenPos' => 8, - 'startFilePos' => 2, - 'endFilePos' => 4, - ], - 'Unknown', - MutatedNode::wrap(new Nop()), - 0, - [] - ), - 'notCovered#0', - $mutantDiff = <<<'DIFF' + $mutationDiff = <<<'DIFF' --- Original +++ New @@ @@ @@ -124,7 +100,19 @@ public function test_it_can_create_a_result_from_a_non_covered_mutant_process(): - echo 'original'; + echo 'notCovered#0'; -DIFF +DIFF; + + $mutantProcess = new MutantProcess( + $processMock, + new Mutation( + $originalFilePath = 'path/to/Foo.php', + $mutatorName = MutatorName::getName(For_::class), + $originalStartingLine = 10, + [], + '0800f', + '/path/to/mutation', + 'notCovered#0', + $mutationDiff ) ); @@ -133,7 +121,7 @@ public function test_it_can_create_a_result_from_a_non_covered_mutant_process(): $processCommandLine, $processOutput, DetectionStatus::NOT_COVERED, - $mutantDiff, + $mutationDiff, $mutatorName, $originalFilePath, $originalStartingLine @@ -163,35 +151,7 @@ public function test_it_can_create_a_result_from_a_timed_out_mutant_process(): v ->method($this->anything()) ; - $mutantProcess = new MutantProcess( - $processMock, - new Mutant( - '/path/to/mutant', - new Mutation( - $originalFilePath = 'path/to/Foo.php', - [], - $mutatorName = MutatorName::getName(For_::class), - [ - 'startLine' => $originalStartingLine = 10, - 'endLine' => 15, - 'startTokenPos' => 0, - 'endTokenPos' => 8, - 'startFilePos' => 2, - 'endFilePos' => 4, - ], - 'Unknown', - MutatedNode::wrap(new Nop()), - 0, - [ - new TestLocation( - 'FooTest::test_it_can_instantiate', - '/path/to/acme/FooTest.php', - 0.01 - ), - ] - ), - 'timedOut#0', - $mutantDiff = <<<'DIFF' + $mutationDiff = <<<'DIFF' --- Original +++ New @@ @@ @@ -199,9 +159,28 @@ public function test_it_can_create_a_result_from_a_timed_out_mutant_process(): v - echo 'original'; + echo 'timedOut#0'; -DIFF +DIFF; + + $mutantProcess = new MutantProcess( + $processMock, + new Mutation( + $originalFilePath = 'path/to/Foo.php', + $mutatorName = MutatorName::getName(For_::class), + $originalStartingLine = 10, + [ + new TestLocation( + 'FooTest::test_it_can_instantiate', + '/path/to/acme/FooTest.php', + 0.01 + ), + ], + '0800f', + '/path/to/mutation', + 'timedOut#0', + $mutationDiff ) ); + $mutantProcess->markAsTimedOut(); $this->assertResultStateIs( @@ -209,7 +188,7 @@ public function test_it_can_create_a_result_from_a_timed_out_mutant_process(): v $processCommandLine, $processOutput, DetectionStatus::TIMED_OUT, - $mutantDiff, + $mutationDiff, $mutatorName, $originalFilePath, $originalStartingLine @@ -244,35 +223,7 @@ public function test_it_can_create_a_result_from_an_errored_mutant_process(): vo ->method($this->anything()) ; - $mutantProcess = new MutantProcess( - $processMock, - new Mutant( - '/path/to/mutant', - new Mutation( - $originalFilePath = 'path/to/Foo.php', - [], - $mutatorName = MutatorName::getName(For_::class), - [ - 'startLine' => $originalStartingLine = 10, - 'endLine' => 15, - 'startTokenPos' => 0, - 'endTokenPos' => 8, - 'startFilePos' => 2, - 'endFilePos' => 4, - ], - 'Unknown', - MutatedNode::wrap(new Nop()), - 0, - [ - new TestLocation( - 'FooTest::test_it_can_instantiate', - '/path/to/acme/FooTest.php', - 0.01 - ), - ] - ), - 'errored#0', - $mutantDiff = <<<'DIFF' + $mutationDiff = <<<'DIFF' --- Original +++ New @@ @@ @@ -280,7 +231,25 @@ public function test_it_can_create_a_result_from_an_errored_mutant_process(): vo - echo 'original'; + echo 'errored#0'; -DIFF +DIFF; + + $mutantProcess = new MutantProcess( + $processMock, + new Mutation( + $originalFilePath = 'path/to/Foo.php', + $mutatorName = MutatorName::getName(For_::class), + $originalStartingLine = 10, + [ + new TestLocation( + 'FooTest::test_it_can_instantiate', + '/path/to/acme/FooTest.php', + 0.01 + ), + ], + '0800f', + '/path/to/mutation', + 'errored#0', + $mutationDiff ) ); @@ -289,7 +258,7 @@ public function test_it_can_create_a_result_from_an_errored_mutant_process(): vo $processCommandLine, $processOutput, DetectionStatus::ERROR, - $mutantDiff, + $mutationDiff, $mutatorName, $originalFilePath, $originalStartingLine @@ -326,35 +295,7 @@ public function test_it_can_crate_a_result_from_an_escaped_mutant_process(): voi ->willReturn(true) ; - $mutantProcess = new MutantProcess( - $processMock, - new Mutant( - '/path/to/mutant', - new Mutation( - $originalFilePath = 'path/to/Foo.php', - [], - $mutatorName = MutatorName::getName(For_::class), - [ - 'startLine' => $originalStartingLine = 10, - 'endLine' => 15, - 'startTokenPos' => 0, - 'endTokenPos' => 8, - 'startFilePos' => 2, - 'endFilePos' => 4, - ], - 'Unknown', - MutatedNode::wrap(new Nop()), - 0, - [ - new TestLocation( - 'FooTest::test_it_can_instantiate', - '/path/to/acme/FooTest.php', - 0.01 - ), - ] - ), - 'escaped#0', - $mutantDiff = <<<'DIFF' + $mutationDiff = <<<'DIFF' --- Original +++ New @@ @@ @@ -362,7 +303,25 @@ public function test_it_can_crate_a_result_from_an_escaped_mutant_process(): voi - echo 'original'; + echo 'escaped#0'; -DIFF +DIFF; + + $mutantProcess = new MutantProcess( + $processMock, + new Mutation( + $originalFilePath = 'path/to/Foo.php', + $mutatorName = MutatorName::getName(For_::class), + $originalStartingLine = 10, + [ + new TestLocation( + 'FooTest::test_it_can_instantiate', + '/path/to/acme/FooTest.php', + 0.01 + ), + ], + '0800f', + '/path/to/mutation', + 'escaped#0', + $mutationDiff ) ); @@ -371,7 +330,7 @@ public function test_it_can_crate_a_result_from_an_escaped_mutant_process(): voi $processCommandLine, 'Tests passed!', DetectionStatus::ESCAPED, - $mutantDiff, + $mutationDiff, $mutatorName, $originalFilePath, $originalStartingLine @@ -408,35 +367,7 @@ public function test_it_can_crate_a_result_from_a_killed_mutant_process(): void ->willReturn(false) ; - $mutantProcess = new MutantProcess( - $processMock, - new Mutant( - '/path/to/mutant', - new Mutation( - $originalFilePath = 'path/to/Foo.php', - [], - $mutatorName = MutatorName::getName(For_::class), - [ - 'startLine' => $originalStartingLine = 10, - 'endLine' => 15, - 'startTokenPos' => 0, - 'endTokenPos' => 8, - 'startFilePos' => 2, - 'endFilePos' => 4, - ], - 'Unknown', - MutatedNode::wrap(new Nop()), - 0, - [ - new TestLocation( - 'FooTest::test_it_can_instantiate', - '/path/to/acme/FooTest.php', - 0.01 - ), - ] - ), - 'killed#0', - $mutantDiff = <<<'DIFF' + $mutationDiff = <<<'DIFF' --- Original +++ New @@ @@ @@ -444,7 +375,25 @@ public function test_it_can_crate_a_result_from_a_killed_mutant_process(): void - echo 'original'; + echo 'killed#0'; -DIFF +DIFF; + + $mutantProcess = new MutantProcess( + $processMock, + new Mutation( + $originalFilePath = 'path/to/Foo.php', + $mutatorName = MutatorName::getName(For_::class), + $originalStartingLine = 10, + [ + new TestLocation( + 'FooTest::test_it_can_instantiate', + '/path/to/acme/FooTest.php', + 0.01 + ), + ], + '0800f', + '/path/to/mutation', + 'killed#0', + $mutationDiff ) ); @@ -453,7 +402,7 @@ public function test_it_can_crate_a_result_from_a_killed_mutant_process(): void $processCommandLine, 'Tests failed!', DetectionStatus::KILLED, - $mutantDiff, + $mutationDiff, $mutatorName, $originalFilePath, $originalStartingLine diff --git a/tests/phpunit/Mutant/MutantExecutionResultTest.php b/tests/phpunit/Mutant/MutantExecutionResultTest.php index 365219dde..a92a9a588 100644 --- a/tests/phpunit/Mutant/MutantExecutionResultTest.php +++ b/tests/phpunit/Mutant/MutantExecutionResultTest.php @@ -37,25 +37,20 @@ use Infection\AbstractTestFramework\Coverage\TestLocation; use Infection\Mutant\DetectionStatus; -use Infection\Mutant\Mutant; use Infection\Mutant\MutantExecutionResult; use Infection\Mutation\Mutation; use Infection\Mutator\ZeroIteration\For_; -use Infection\PhpParser\MutatedNode; use Infection\Tests\Mutator\MutatorName; -use PhpParser\Node\Stmt\Nop; use PHPUnit\Framework\TestCase; final class MutantExecutionResultTest extends TestCase { - use MutantExecutionResultAssertions; - public function test_it_can_be_instantiated(): void { $processCommandLine = 'bin/phpunit --configuration infection-tmp-phpunit.xml --filter "tests/Acme/FooTest.php"'; $processOutput = 'Passed!'; $processResultCode = DetectionStatus::ESCAPED; - $mutantDiff = <<<'DIFF' + $mutationDiff = <<<'DIFF' --- Original +++ New @@ @@ @@ -73,7 +68,7 @@ public function test_it_can_be_instantiated(): void $processCommandLine, $processOutput, $processResultCode, - $mutantDiff, + $mutationDiff, $mutatorName, $originalFilePath, $originalStartingLine @@ -84,42 +79,16 @@ public function test_it_can_be_instantiated(): void $processCommandLine, $processOutput, $processResultCode, - $mutantDiff, + $mutationDiff, $mutatorName, $originalFilePath, $originalStartingLine ); } - public function test_it_can_be_instantiated_from_a_non_covered_mutant(): void + public function test_it_can_be_instantiated_from_a_mutation_non_covered_by_tests(): void { - $mutant = new Mutant( - '/path/to/mutant', - new Mutation( - $originalFilePath = 'path/to/Foo.php', - [], - $mutatorName = MutatorName::getName(For_::class), - [ - 'startLine' => $originalStartingLine = 10, - 'endLine' => 15, - 'startTokenPos' => 0, - 'endTokenPos' => 8, - 'startFilePos' => 2, - 'endFilePos' => 4, - ], - 'Unknown', - MutatedNode::wrap(new Nop()), - 0, - [ - new TestLocation( - 'FooTest::test_it_can_instantiate', - '/path/to/acme/FooTest.php', - 0.01 - ), - ] - ), - 'notCovered#0', - $mutantDiff = <<<'DIFF' + $mutationDiff = <<<'DIFF' --- Original +++ New @@ @@ @@ -127,15 +96,31 @@ public function test_it_can_be_instantiated_from_a_non_covered_mutant(): void - echo 'original'; + echo 'notCovered#0'; -DIFF +DIFF; + + $mutation = new Mutation( + $originalFilePath = 'path/to/Foo.php', + $mutatorName = MutatorName::getName(For_::class), + $originalStartingLine = 10, + [ + new TestLocation( + 'FooTest::test_it_can_instantiate', + '/path/to/acme/FooTest.php', + 0.01 + ), + ], + '0800f', + '/path/to/mutation', + 'notCovered#0', + $mutationDiff ); $this->assertResultStateIs( - MutantExecutionResult::createFromNonCoveredMutant($mutant), + MutantExecutionResult::createFromNonCoveredByTestsMutation($mutation), '', '', DetectionStatus::NOT_COVERED, - $mutantDiff, + $mutationDiff, $mutatorName, $originalFilePath, $originalStartingLine @@ -147,7 +132,7 @@ private function assertResultStateIs( string $expectedProcessCommandLine, string $expectedProcessOutput, string $expectedDetectionStatus, - string $expectedMutantDiff, + string $expectedMutationDiff, string $expectedMutatorName, string $expectedOriginalFilePath, int $expectedOriginalStartingLine @@ -155,7 +140,7 @@ private function assertResultStateIs( $this->assertSame($expectedProcessCommandLine, $result->getProcessCommandLine()); $this->assertSame($expectedProcessOutput, $result->getProcessOutput()); $this->assertSame($expectedDetectionStatus, $result->getDetectionStatus()); - $this->assertSame($expectedMutantDiff, $result->getMutantDiff()); + $this->assertSame($expectedMutationDiff, $result->getMutationDiff()); $this->assertSame($expectedMutatorName, $result->getMutatorName()); $this->assertSame($expectedOriginalFilePath, $result->getOriginalFilePath()); $this->assertSame($expectedOriginalStartingLine, $result->getOriginalStartingLine()); diff --git a/tests/phpunit/Mutant/MutantFactoryTest.php b/tests/phpunit/Mutant/MutantFactoryTest.php deleted file mode 100644 index cd6a11757..000000000 --- a/tests/phpunit/Mutant/MutantFactoryTest.php +++ /dev/null @@ -1,197 +0,0 @@ -codeFactoryMock = $this->createMock(MutantCodeFactory::class); - - $this->printerMock = $this->createMock(PrettyPrinterAbstract::class); - - $this->differMock = $this->createMock(Differ::class); - - $this->mutantFactory = new MutantFactory( - '/path/to/tmp', - $this->differMock, - $this->printerMock, - $this->codeFactoryMock - ); - } - - public function test_it_creates_a_mutant_instance_from_the_given_mutation(): void - { - $mutation = self::createMutation( - $originalNodes = [new Node\Stmt\Namespace_( - new Node\Name('Acme'), - [new Node\Scalar\LNumber(0)] - )], - $tests = [ - new TestLocation( - 'FooTest::test_it_can_instantiate', - '/path/to/acme/FooTest.php', - 0.01 - ), - ] - ); - - $expectedMutantFilePath = sprintf( - '/path/to/tmp/mutant.%s.infection.php', - $mutation->getHash() - ); - - $this->codeFactoryMock - ->expects($this->once()) - ->method('createCode') - ->with($mutation) - ->willReturn('mutated code') - ; - - $this->printerMock - ->expects($this->once()) - ->method('prettyPrintFile') - ->with($originalNodes) - ->willReturn('original code') - ; - - $this->differMock - ->expects($this->once()) - ->method('diff') - ->with('original code', 'mutated code') - ->willReturn('code diff') - ; - - $mutant = $this->mutantFactory->create($mutation); - - $this->assertMutantStateIs( - $mutant, - $expectedMutantFilePath, - $mutation, - 'mutated code', - 'code diff', - true, - $tests - ); - } - - public function test_it_printing_the_original_file_is_memoized(): void - { - $mutation = self::createMutation( - $originalNodes = [new Node\Stmt\Nop()], - [] - ); - - $this->printerMock - ->expects($this->once()) - ->method('prettyPrintFile') - ->with($originalNodes) - ->willReturn('original code') - ; - - $this->differMock - ->expects($this->atLeastOnce()) - ->method('diff') - ->willReturn('code diff') - ; - - $this->mutantFactory->create($mutation); - $this->mutantFactory->create($mutation); - } - - /** - * @param Node[] $originalNodes - * @param TestLocation[] $tests - */ - private static function createMutation(array $originalNodes, array $tests): Mutation - { - return new Mutation( - '/path/to/acme/Foo.php', - $originalNodes, - MutatorName::getName(Plus::class), - [ - 'startLine' => 3, - 'endLine' => 5, - 'startTokenPos' => 21, - 'endTokenPos' => 31, - 'startFilePos' => 43, - 'endFilePos' => 53, - ], - Node\Scalar\LNumber::class, - MutatedNode::wrap(new Node\Scalar\LNumber(1)), - 0, - $tests - ); - } -} diff --git a/tests/phpunit/Mutant/MutantTest.php b/tests/phpunit/Mutant/MutantTest.php deleted file mode 100644 index a857b9bc0..000000000 --- a/tests/phpunit/Mutant/MutantTest.php +++ /dev/null @@ -1,138 +0,0 @@ -assertMutantStateIs( - $mutant, - $filePath, - $mutation, - $mutatedCode, - $diff, - $expectedCoveredByTests, - $expectedTests - ); - } - - public function valuesProvider(): iterable - { - $nominalAttributes = [ - 'startLine' => 3, - 'endLine' => 5, - 'startTokenPos' => 21, - 'endTokenPos' => 31, - 'startFilePos' => 43, - 'endFilePos' => 53, - ]; - - $tests = [ - new TestLocation( - 'FooTest::test_it_can_instantiate', - '/path/to/acme/FooTest.php', - 0.01 - ), - ]; - - yield 'nominal with tests' => [ - '/path/to/tmp/mutant.Foo.infection.php', - new Mutation( - '/path/to/acme/Foo.php', - [new Node\Stmt\Namespace_( - new Node\Name('Acme'), - [new Node\Scalar\LNumber(0)] - )], - MutatorName::getName(Plus::class), - $nominalAttributes, - Node\Scalar\LNumber::class, - MutatedNode::wrap(new Node\Scalar\LNumber(1)), - 0, - $tests - ), - 'mutated code', - 'diff value', - true, - $tests, - ]; - - yield 'nominal without tests' => [ - '/path/to/tmp/mutant.Foo.infection.php', - new Mutation( - '/path/to/acme/Foo.php', - [new Node\Stmt\Namespace_( - new Node\Name('Acme'), - [new Node\Scalar\LNumber(0)] - )], - MutatorName::getName(Plus::class), - $nominalAttributes, - Node\Scalar\LNumber::class, - MutatedNode::wrap(new Node\Scalar\LNumber(1)), - 0, - [] - ), - 'mutated code', - 'diff value', - false, - [], - ]; - } -} diff --git a/tests/phpunit/Mutant/MutationFactoryTest.php b/tests/phpunit/Mutant/MutationFactoryTest.php new file mode 100644 index 000000000..806c56efc --- /dev/null +++ b/tests/phpunit/Mutant/MutationFactoryTest.php @@ -0,0 +1,277 @@ +codeFactoryMock = $this->createMock(MutantCodeFactory::class); + $this->printerMock = $this->createMock(PrettyPrinterAbstract::class); + $this->differMock = $this->createMock(Differ::class); + + $this->mutationFactory = new MutationFactory( + '/path/to/tmp', + $this->differMock, + $this->printerMock, + $this->codeFactoryMock + ); + } + + /** + * @dataProvider valuesProvider + * + * @param Node[] $originalFileAst + * @param array $attributes + * @param class-string $mutatedNodeClass + * @param TestLocation[] $tests + * @param array $expectedFilteredAttributes + */ + public function test_it_creates_a_mutation( + string $originalFilePath, + array $originalFileAst, + string $mutatorName, + array $attributes, + string $mutatedNodeClass, + MutatedNode $mutatedNode, + int $mutationByMutatorIndex, + array $tests, + array $expectedFilteredAttributes, + string $expectedHash, + string $expectedMutationFilePath, + int $expectedOriginalStartingLine + ): void { + $this->codeFactoryMock + ->expects($this->exactly(2)) + ->method('createCode') + ->with( + $expectedFilteredAttributes, + $originalFileAst, + $mutatedNodeClass, + $mutatedNode + ) + ->willReturn('mutated code') + ; + + $this->printerMock + ->expects($this->once()) + ->method('prettyPrintFile') + ->with($originalFileAst) + ->willReturn('original code') + ; + + $this->differMock + ->expects($this->exactly(2)) + ->method('diff') + ->with('original code', 'mutated code') + ->willReturn('code diff') + ; + + $mutation1 = $this->mutationFactory->create( + $originalFilePath, + $originalFileAst, + $mutatorName, + $attributes, + $mutatedNodeClass, + $mutatedNode, + $mutationByMutatorIndex, + $tests + ); + + $this->assertMutationSateIs( + $mutation1, + $originalFilePath, + $mutatorName, + $tests, + $expectedHash, + $expectedMutationFilePath, + 'mutated code', + 'code diff', + $expectedOriginalStartingLine, + true + ); + + // Check memoization + + $mutation2 = $this->mutationFactory->create( + $originalFilePath, + $originalFileAst, + $mutatorName, + $attributes, + $mutatedNodeClass, + $mutatedNode, + $mutationByMutatorIndex, + $tests + ); + + $this->assertMutationSateIs( + $mutation2, + $originalFilePath, + $mutatorName, + $tests, + $expectedHash, + $expectedMutationFilePath, + 'mutated code', + 'code diff', + $expectedOriginalStartingLine, + true + ); + } + + public static function valuesProvider(): iterable + { + $nominalAttributes = [ + 'startLine' => $originalStartingLine = 3, + 'endLine' => 5, + 'startTokenPos' => 21, + 'endTokenPos' => 31, + 'startFilePos' => 43, + 'endFilePos' => 53, + ]; + + yield 'nominal' => (static function () use ( + $nominalAttributes, + $originalStartingLine + ): array { + $expectedHash = md5('/path/to/acme/Foo.php_Plus_0_3_5_21_31_43_53'); + + return [ + '/path/to/acme/Foo.php', + [ + new Node\Stmt\Namespace_( + new Node\Name('Acme'), + [new Node\Scalar\LNumber(0)] + ), + ], + MutatorName::getName(Plus::class), + $nominalAttributes, + Node\Scalar\LNumber::class, + MutatedNode::wrap(new Node\Scalar\LNumber(1)), + 0, + [ + new TestLocation( + 'FooTest::test_it_can_instantiate', + '/path/to/acme/FooTest.php', + 0.01 + ), + ], + $nominalAttributes, + $expectedHash, + sprintf( + '/path/to/tmp/mutation.%s.infection.php', + $expectedHash + ), + $originalStartingLine, + ]; + })(); + + yield 'with additional attributes' => (static function () use ( + $nominalAttributes, + $originalStartingLine + ): array { + $expectedHash = md5('/path/to/acme/Foo.php_Plus_0_3_5_21_31_43_53'); + + return [ + '/path/to/acme/Foo.php', + [ + new Node\Stmt\Namespace_( + new Node\Name('Acme'), + [new Node\Scalar\LNumber(0)] + ), + ], + MutatorName::getName(Plus::class), + array_merge($nominalAttributes, ['foo' => 100, 'bar' => 1000]), + Node\Scalar\LNumber::class, + MutatedNode::wrap(new Node\Scalar\LNumber(1)), + 0, + [ + new TestLocation( + 'FooTest::test_it_can_instantiate', + '/path/to/acme/FooTest.php', + 0.01 + ), + ], + $nominalAttributes, + $expectedHash, + sprintf( + '/path/to/tmp/mutation.%s.infection.php', + $expectedHash + ), + $originalStartingLine, + ]; + })(); + } +} diff --git a/tests/phpunit/Mutation/FileMutationGeneratorTest.php b/tests/phpunit/Mutation/FileMutationGeneratorTest.php index 705122fa0..7a201a9b4 100644 --- a/tests/phpunit/Mutation/FileMutationGeneratorTest.php +++ b/tests/phpunit/Mutation/FileMutationGeneratorTest.php @@ -36,6 +36,7 @@ namespace Infection\Tests\Mutation; use function current; +use Infection\Container; use Infection\Mutation\FileMutationGenerator; use Infection\Mutation\Mutation; use Infection\Mutator\Arithmetic\Plus; @@ -49,7 +50,6 @@ use Infection\Tests\Fixtures\PhpParser\FakeIgnorer; use Infection\Tests\Fixtures\PhpParser\FakeNode; use Infection\Tests\Mutator\MutatorName; -use Infection\Tests\SingletonContainer; use PhpParser\NodeTraverserInterface; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -70,6 +70,11 @@ final class FileMutationGeneratorTest extends TestCase */ private $traverserFactoryMock; + /** + * @var \Infection\Mutant\MutationFactory|MockObject + */ + private $mutationFactoryMock; + /** * @var FileMutationGenerator */ @@ -79,16 +84,41 @@ protected function setUp(): void { $this->fileParserMock = $this->createMock(FileParser::class); $this->traverserFactoryMock = $this->createMock(NodeTraverserFactory::class); + $this->mutationFactoryMock = $this->createMock(\Infection\Mutant\MutationFactory::class); $this->mutationGenerator = new FileMutationGenerator( $this->fileParserMock, $this->traverserFactoryMock, - new LineRangeCalculator() + new LineRangeCalculator(), + $this->mutationFactoryMock ); } public function test_it_generates_mutations_for_a_given_file(): void { + $container = Container::create()->withDynamicParameters( + null, + '', + false, + 'default', + false, + false, + 'dot', + false, + '/path/to/coverage', + '', + false, + false, + .0, + .0, + 2, + 'phpunit', + '', + '', + 0, + true + ); + $traceMock = $this->createTraceMock( self::FIXTURES_DIR . '/Mutation/OneFile/OneFile.php', '', @@ -104,9 +134,7 @@ public function test_it_generates_mutations_for_a_given_file(): void ->willReturn([]) ; - $mutationGenerator = SingletonContainer::getContainer()->getFileMutationGenerator(); - - $mutations = $mutationGenerator->generate( + $mutations = $container->getFileMutationGenerator()->generate( $traceMock, false, [new IgnoreMutator(new IgnoreConfig([]), new Plus())], @@ -185,18 +213,24 @@ public function test_it_skips_the_mutation_generation_if_checks_only_covered_cod ): void { $this->fileParserMock ->expects($this->never()) - ->method('parse') + ->method($this->anything()) ; $this->traverserFactoryMock ->expects($this->never()) - ->method('create') + ->method($this->anything()) + ; + + $this->mutationFactoryMock + ->expects($this->never()) + ->method($this->anything()) ; $mutationGenerator = new FileMutationGenerator( $this->fileParserMock, $this->traverserFactoryMock, - new LineRangeCalculator() + new LineRangeCalculator(), + $this->mutationFactoryMock ); $mutations = $mutationGenerator->generate( diff --git a/tests/phpunit/Mutant/MutantExecutionResultAssertions.php b/tests/phpunit/Mutation/MutantExecutionResultAssertions.php similarity index 94% rename from tests/phpunit/Mutant/MutantExecutionResultAssertions.php rename to tests/phpunit/Mutation/MutantExecutionResultAssertions.php index 316d71bde..a638db98c 100644 --- a/tests/phpunit/Mutant/MutantExecutionResultAssertions.php +++ b/tests/phpunit/Mutation/MutantExecutionResultAssertions.php @@ -33,7 +33,7 @@ declare(strict_types=1); -namespace Infection\Tests\Mutant; +namespace Infection\Tests\Mutation; use Infection\Mutant\MutantExecutionResult; @@ -44,7 +44,7 @@ private function assertResultStateIs( string $expectedProcessCommandLine, string $expectedProcessOutput, string $expectedDetectionStatus, - string $expectedMutantDiff, + string $expectedMutationDiff, string $expectedMutatorName, string $expectedOriginalFilePath, int $expectedOriginalStartingLine @@ -52,7 +52,7 @@ private function assertResultStateIs( $this->assertSame($expectedProcessCommandLine, $result->getProcessCommandLine()); $this->assertSame($expectedProcessOutput, $result->getProcessOutput()); $this->assertSame($expectedDetectionStatus, $result->getDetectionStatus()); - $this->assertSame($expectedMutantDiff, $result->getMutantDiff()); + $this->assertSame($expectedMutationDiff, $result->getMutationDiff()); $this->assertSame($expectedMutatorName, $result->getMutatorName()); $this->assertSame($expectedOriginalFilePath, $result->getOriginalFilePath()); $this->assertSame($expectedOriginalStartingLine, $result->getOriginalStartingLine()); diff --git a/tests/phpunit/Mutant/MutantAssertions.php b/tests/phpunit/Mutation/MutationAssertions.php similarity index 62% rename from tests/phpunit/Mutant/MutantAssertions.php rename to tests/phpunit/Mutation/MutationAssertions.php index e850267be..1639e3e40 100644 --- a/tests/phpunit/Mutant/MutantAssertions.php +++ b/tests/phpunit/Mutation/MutationAssertions.php @@ -33,31 +33,32 @@ declare(strict_types=1); -namespace Infection\Tests\Mutant; +namespace Infection\Tests\Mutation; -use Infection\AbstractTestFramework\Coverage\TestLocation; -use Infection\Mutant\Mutant; use Infection\Mutation\Mutation; -trait MutantAssertions +trait MutationAssertions { - /** - * @param TestLocation[] $expectedTests - */ - public function assertMutantStateIs( - Mutant $mutant, + private function assertMutationSateIs( + Mutation $mutation, + string $expectedOriginalFilePath, + string $expectedMutatorName, + array $expectedTests, + string $expectedHash, string $expectedFilePath, - Mutation $expectedMutation, - string $expectedMutatedCode, + string $expectedCode, string $expectedDiff, - bool $expectedCoveredByTests, - array $expectedTests + int $expectedOriginalStartingLine, + bool $expectedHasTests ): void { - $this->assertSame($expectedFilePath, $mutant->getFilePath()); - $this->assertSame($expectedMutation, $mutant->getMutation()); - $this->assertSame($expectedMutatedCode, $mutant->getMutatedCode()); - $this->assertSame($expectedDiff, $mutant->getDiff()); - $this->assertSame($expectedCoveredByTests, $mutant->isCoveredByTest()); - $this->assertSame($expectedTests, $mutant->getTests()); + $this->assertSame($expectedOriginalFilePath, $mutation->getOriginalFilePath()); + $this->assertSame($expectedMutatorName, $mutation->getMutatorName()); + $this->assertSame($expectedOriginalStartingLine, $mutation->getOriginalStartingLine()); + $this->assertSame($expectedTests, $mutation->getAllTests()); + $this->assertSame($expectedHasTests, $mutation->isCoveredByTest()); + $this->assertSame($expectedHash, $mutation->getHash()); + $this->assertSame($expectedFilePath, $mutation->getFilePath()); + $this->assertSame($expectedCode, $mutation->getMutatedCode()); + $this->assertSame($expectedDiff, $mutation->getDiff()); } } diff --git a/tests/phpunit/Mutation/MutationTest.php b/tests/phpunit/Mutation/MutationTest.php index 4cb66f5e3..8b0d99f13 100644 --- a/tests/phpunit/Mutation/MutationTest.php +++ b/tests/phpunit/Mutation/MutationTest.php @@ -35,14 +35,10 @@ namespace Infection\Tests\Mutation; -use function array_merge; use Infection\AbstractTestFramework\Coverage\TestLocation; use Infection\Mutation\Mutation; use Infection\Mutator\Arithmetic\Plus; -use Infection\PhpParser\MutatedNode; use Infection\Tests\Mutator\MutatorName; -use function md5; -use PhpParser\Node; use PHPUnit\Framework\TestCase; final class MutationTest extends TestCase @@ -50,85 +46,59 @@ final class MutationTest extends TestCase /** * @dataProvider valuesProvider * - * @param Node[] $originalFileAst - * @param array $attributes - * @param array $expectedAttributes * @param TestLocation[] $tests */ public function test_it_can_be_instantiated( string $originalFilePath, - array $originalFileAst, string $mutatorName, - array $attributes, - string $mutatedNodeClass, - MutatedNode $mutatedNode, - int $mutationByMutatorIndex, + int $originalStartingLine, array $tests, - array $expectedAttributes, - int $expectedOriginalStartingLine, - bool $expectedCoveredByTests, - string $expectedHash + string $hash, + string $filePath, + string $code, + string $diff, + bool $expectedHasTests ): void { $mutation = new Mutation( $originalFilePath, - $originalFileAst, $mutatorName, - $attributes, - $mutatedNodeClass, - $mutatedNode, - $mutationByMutatorIndex, - $tests + $originalStartingLine, + $tests, + $hash, + $filePath, + $code, + $diff ); $this->assertSame($originalFilePath, $mutation->getOriginalFilePath()); - $this->assertSame($originalFileAst, $mutation->getOriginalFileAst()); $this->assertSame($mutatorName, $mutation->getMutatorName()); - $this->assertSame($expectedAttributes, $mutation->getAttributes()); - $this->assertSame($expectedOriginalStartingLine, $mutation->getOriginalStartingLine()); - $this->assertSame($mutatedNodeClass, $mutation->getMutatedNodeClass()); - $this->assertSame($mutatedNode, $mutation->getMutatedNode()); + $this->assertSame($originalStartingLine, $mutation->getOriginalStartingLine()); $this->assertSame($tests, $mutation->getAllTests()); - $this->assertSame($expectedCoveredByTests, $mutation->isCoveredByTest()); - $this->assertSame($expectedHash, $mutation->getHash()); + $this->assertSame($expectedHasTests, $mutation->isCoveredByTest()); + $this->assertSame($hash, $mutation->getHash()); + $this->assertSame($filePath, $mutation->getFilePath()); + $this->assertSame($code, $mutation->getMutatedCode()); + $this->assertSame($diff, $mutation->getDiff()); } public function valuesProvider(): iterable { - $nominalAttributes = [ - 'startLine' => $originalStartingLine = 3, - 'endLine' => 5, - 'startTokenPos' => 21, - 'endTokenPos' => 31, - 'startFilePos' => 43, - 'endFilePos' => 53, - ]; - yield 'empty' => [ '', - [], MutatorName::getName(Plus::class), - $nominalAttributes, - Node\Scalar\LNumber::class, - MutatedNode::wrap(new Node\Scalar\LNumber(1)), - -1, + 3, [], - $nominalAttributes, - $originalStartingLine, + '', + '', + '', + '', false, - md5('_Plus_-1_3_5_21_31_43_53'), ]; yield 'nominal with a test' => [ '/path/to/acme/Foo.php', - [new Node\Stmt\Namespace_( - new Node\Name('Acme'), - [new Node\Scalar\LNumber(0)] - )], MutatorName::getName(Plus::class), - $nominalAttributes, - Node\Scalar\LNumber::class, - MutatedNode::wrap(new Node\Scalar\LNumber(1)), - 0, + 3, [ new TestLocation( 'FooTest::test_it_can_instantiate', @@ -136,103 +106,41 @@ public function valuesProvider(): iterable 0.01 ), ], - $nominalAttributes, - $originalStartingLine, - true, - md5('/path/to/acme/Foo.php_Plus_0_3_5_21_31_43_53'), - ]; + '0800f', + '/path/to/mutation', + 'notCovered#0', + <<<'DIFF' +--- Original ++++ New +@@ @@ - yield 'nominal with a test with a different mutator index' => [ - '/path/to/acme/Foo.php', - [new Node\Stmt\Namespace_( - new Node\Name('Acme'), - [new Node\Scalar\LNumber(0)] - )], - MutatorName::getName(Plus::class), - $nominalAttributes, - Node\Scalar\LNumber::class, - MutatedNode::wrap(new Node\Scalar\LNumber(1)), - 99, - [ - new TestLocation( - 'FooTest::test_it_can_instantiate', - '/path/to/acme/FooTest.php', - 0.01 - ), - ], - $nominalAttributes, - $originalStartingLine, - true, - md5('/path/to/acme/Foo.php_Plus_99_3_5_21_31_43_53'), - ]; +- echo 'original'; ++ echo 'notCovered#0'; - yield 'nominal with a test and additional attributes' => [ - '/path/to/acme/Foo.php', - [new Node\Stmt\Namespace_( - new Node\Name('Acme'), - [new Node\Scalar\LNumber(0)] - )], - MutatorName::getName(Plus::class), - array_merge($nominalAttributes, ['foo' => 100, 'bar' => 1000]), - Node\Scalar\LNumber::class, - MutatedNode::wrap(new Node\Scalar\LNumber(1)), - 0, - [ - new TestLocation( - 'FooTest::test_it_can_instantiate', - '/path/to/acme/FooTest.php', - 0.01 - ), - ], - $nominalAttributes, - $originalStartingLine, +DIFF + , true, - md5('/path/to/acme/Foo.php_Plus_0_3_5_21_31_43_53'), ]; yield 'nominal without a test' => [ '/path/to/acme/Foo.php', - [new Node\Stmt\Namespace_( - new Node\Name('Acme'), - [new Node\Scalar\LNumber(0)] - )], MutatorName::getName(Plus::class), - $nominalAttributes, - Node\Scalar\LNumber::class, - MutatedNode::wrap(new Node\Scalar\LNumber(1)), - 0, + 3, [], - $nominalAttributes, - $originalStartingLine, - false, - md5('/path/to/acme/Foo.php_Plus_0_3_5_21_31_43_53'), - ]; + '0800f', + '/path/to/mutation', + 'notCovered#0', + <<<'DIFF' +--- Original ++++ New +@@ @@ - yield 'nominal with a test and multiple mutated nodes' => [ - '/path/to/acme/Foo.php', - [new Node\Stmt\Namespace_( - new Node\Name('Acme'), - [new Node\Scalar\LNumber(0)] - )], - MutatorName::getName(Plus::class), - $nominalAttributes, - Node\Scalar\LNumber::class, - MutatedNode::wrap([ - new Node\Scalar\LNumber(1), - new Node\Scalar\LNumber(-1), - ]), - 0, - [ - new TestLocation( - 'FooTest::test_it_can_instantiate', - '/path/to/acme/FooTest.php', - 0.01 - ), - ], - $nominalAttributes, - $originalStartingLine, - true, - md5('/path/to/acme/Foo.php_Plus_0_3_5_21_31_43_53'), +- echo 'original'; ++ echo 'notCovered#0'; + +DIFF + , + false, ]; } } diff --git a/tests/phpunit/Mutator/BaseMutatorTestCase.php b/tests/phpunit/Mutator/BaseMutatorTestCase.php index b6b2b561d..2fbbdd19c 100644 --- a/tests/phpunit/Mutator/BaseMutatorTestCase.php +++ b/tests/phpunit/Mutator/BaseMutatorTestCase.php @@ -80,19 +80,19 @@ final public function doTest(string $inputCode, $expectedCode = [], array $setti $this->fail('Input code cant be the same as mutated code'); } - $mutants = $this->mutate($inputCode, $settings); + $mutatedCodes = $this->mutate($inputCode, $settings); $this->assertCount( - count($mutants), + count($mutatedCodes), $expectedCodeSamples, sprintf( - 'Failed asserting that the number of code samples (%d) equals the number of mutants (%d) created by the mutator.', + 'Failed asserting that the number of code samples (%d) equals the number of mutations (%d) created by the mutator.', count($expectedCodeSamples), - count($mutants) + count($mutatedCodes) ) ); - foreach ($mutants as $realMutatedCode) { + foreach ($mutatedCodes as $realMutatedCode) { /** @var string|null $expectedCodeSample */ $expectedCodeSample = array_shift($expectedCodeSamples); @@ -133,21 +133,25 @@ final protected function mutate(string $code, array $settings = []): array $traverser = new NodeTraverser(); $traverser->addVisitor(new CloneVisitor()); - $mutants = []; + $mutatedCodes = []; foreach ($mutations as $mutation) { - $mutatorVisitor = new MutatorVisitor($mutation); + $mutatorVisitor = new MutatorVisitor( + $mutation->getAttributes(), + $mutation->getMutatedNodeClass(), + $mutation->getMutatedNode() + ); $traverser->addVisitor($mutatorVisitor); $mutatedStatements = $traverser->traverse($mutation->getOriginalFileAst()); - $mutants[] = SingletonContainer::getPrinter()->prettyPrintFile($mutatedStatements); + $mutatedCodes[] = SingletonContainer::getPrinter()->prettyPrintFile($mutatedStatements); $traverser->removeVisitor($mutatorVisitor); } - return $mutants; + return $mutatedCodes; } /** diff --git a/tests/phpunit/Mutator/MutatorParserTest.php b/tests/phpunit/Mutator/MutatorParserTest.php index ef7175ff4..e4e96e6d7 100644 --- a/tests/phpunit/Mutator/MutatorParserTest.php +++ b/tests/phpunit/Mutator/MutatorParserTest.php @@ -37,6 +37,7 @@ use Infection\Mutator\MutatorParser; use PHPUnit\Framework\TestCase; +use UnexpectedValueException; final class MutatorParserTest extends TestCase { @@ -64,6 +65,14 @@ public function test_it_can_parse_the_provided_input( $this->assertSame($expectedMutators, $parsedMutators); } + public function test_it_cannot_parse_unknown_mutator(): void + { + $this->expectException(UnexpectedValueException::class); + $this->expectExceptionMessage('Expected "Unknown" to be a known mutator or profile. See "https://infection.github.io/guide/mutators.html" and "https://infection.github.io/guide/profiles.html" for the list of available mutants and profiles.'); + + $this->mutatorParser->parse('Unknown'); + } + public function mutatorInputProvider(): iterable { yield 'empty string' => ['', []]; diff --git a/tests/phpunit/PhpParser/Visitor/MutatorVisitorTest.php b/tests/phpunit/PhpParser/Visitor/MutatorVisitorTest.php index 6f310c8ee..fe81f781e 100644 --- a/tests/phpunit/PhpParser/Visitor/MutatorVisitorTest.php +++ b/tests/phpunit/PhpParser/Visitor/MutatorVisitorTest.php @@ -35,7 +35,6 @@ namespace Infection\Tests\PhpParser\Visitor; -use Infection\Mutation\Mutation; use Infection\Mutator\FunctionSignature\PublicVisibility; use Infection\PhpParser\MutatedNode; use Infection\PhpParser\Visitor\MutatorVisitor; @@ -57,15 +56,19 @@ final class MutatorVisitorTest extends BaseVisitorTest * @dataProvider providesMutationCases * * @param Node[] $nodes + * @param array $attributes + * @param class-string $mutatedNodeClass */ public function test_it_mutates_the_correct_node( array $nodes, - string $expectedCodeOutput, - Mutation $mutation + array $attributes, + string $mutatedNodeClass, + MutatedNode $mutatedNode, + string $expectedCodeOutput ): void { $this->traverse( $nodes, - [new MutatorVisitor($mutation)] + [new MutatorVisitor($attributes, $mutatedNodeClass, $mutatedNode)] ); $output = SingletonContainer::getPrinter()->prettyPrintFile($nodes); @@ -77,7 +80,7 @@ public function providesMutationCases(): iterable { yield 'it mutates the correct node' => (function () { return [ - $nodes = $this->parseCode(<<<'PHP' + $this->parseCode(<<<'PHP' 29, + 'endTokenPos' => 48, + 'startLine' => -1, + 'endLine' => -1, + 'startFilePos' => -1, + 'endFilePos' => -1, + ], + ClassMethod::class, + MutatedNode::wrap(new Nop()), <<<'PHP' 29, - 'endTokenPos' => 48, - 'startLine' => -1, - 'endLine' => -1, - 'startFilePos' => -1, - 'endFilePos' => -1, - ], - ClassMethod::class, - MutatedNode::wrap(new Nop()), - 0, - [] - ), ]; })(); yield 'it can mutate the node with multiple-ones' => (function () { return [ - $nodes = $this->parseCode(<<<'PHP' + $this->parseCode(<<<'PHP' 29, + 'endTokenPos' => 48, + 'startLine' => -1, + 'endLine' => -1, + 'startFilePos' => -1, + 'endFilePos' => -1, + ], + ClassMethod::class, + MutatedNode::wrap([new Nop(), new Nop()]), <<<'PHP' 29, - 'endTokenPos' => 48, - 'startLine' => -1, - 'endLine' => -1, - 'startFilePos' => -1, - 'endFilePos' => -1, - ], - ClassMethod::class, - MutatedNode::wrap([new Nop(), new Nop()]), - 0, - [] - ), ]; })(); yield 'it does not mutate if only one of start or end position is correctly set' => (function () { return [ - $nodes = $this->parseCode(<<<'PHP' + $this->parseCode(<<<'PHP' 29, + 'endTokenPos' => 50, + 'startLine' => -1, + 'endLine' => -1, + 'startFilePos' => -1, + 'endFilePos' => -1, + ], + ClassMethod::class, + MutatedNode::wrap(new Nop()), <<<'PHP' 29, - 'endTokenPos' => 50, - 'startLine' => -1, - 'endLine' => -1, - 'startFilePos' => -1, - 'endFilePos' => -1, - ], - ClassMethod::class, - MutatedNode::wrap(new Nop()), - 0, - [] - ), ]; })(); @@ -248,7 +230,7 @@ public function bye() : string $badParser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7, $badLexer); return [ - $nodes = $badParser->parse(<<<'PHP' + $badParser->parse(<<<'PHP' 29, + 'endTokenPos' => 48, + 'startLine' => -1, + 'endLine' => -1, + 'startFilePos' => -1, + 'endFilePos' => -1, + ], + MutatorName::getName(PublicVisibility::class), + MutatedNode::wrap(new Nop()), <<<'PHP' 29, - 'endTokenPos' => 48, - 'startLine' => -1, - 'endLine' => -1, - 'startFilePos' => -1, - 'endFilePos' => -1, - ], - MutatorName::getName(PublicVisibility::class), - MutatedNode::wrap(new Nop()), - 0, - [] - ), ]; })(); } diff --git a/tests/phpunit/Process/Factory/MutantProcessFactoryTest.php b/tests/phpunit/Process/Factory/MutantProcessFactoryTest.php index 22a386cfa..0126fd73d 100644 --- a/tests/phpunit/Process/Factory/MutantProcessFactoryTest.php +++ b/tests/phpunit/Process/Factory/MutantProcessFactoryTest.php @@ -39,50 +39,38 @@ use Infection\AbstractTestFramework\Coverage\TestLocation; use Infection\AbstractTestFramework\TestFrameworkAdapter; use Infection\Event\MutantProcessWasFinished; -use Infection\Mutant\Mutant; use Infection\Mutant\MutantExecutionResult; use Infection\Mutant\MutantExecutionResultFactory; use Infection\Mutation\Mutation; use Infection\Mutator\ZeroIteration\For_; -use Infection\PhpParser\MutatedNode; use Infection\Process\Factory\MutantProcessFactory; use Infection\Tests\Fixtures\Event\EventDispatcherCollector; use Infection\Tests\Mutator\MutatorName; use const PHP_OS_FAMILY; -use PhpParser\Node\Stmt\Nop; use PHPUnit\Framework\TestCase; final class MutantProcessFactoryTest extends TestCase { public function test_it_creates_a_process_with_timeout(): void { - $mutant = new Mutant( - $mutantFilePath = '/path/to/mutant', - new Mutation( - $originalFilePath = 'path/to/Foo.php', - [], - MutatorName::getName(For_::class), - [ - 'startLine' => $originalStartingLine = 10, - 'endLine' => 15, - 'startTokenPos' => 0, - 'endTokenPos' => 8, - 'startFilePos' => 2, - 'endFilePos' => 4, - ], - 'Unknown', - MutatedNode::wrap(new Nop()), - 0, - $tests = [ - new TestLocation( - 'FooTest::test_it_can_instantiate', - '/path/to/acme/FooTest.php', - 0.01 - ), - ] - ), - 'killed#0', - $mutantDiff = <<<'DIFF' + $hash = '0800f'; + $mutationFilePath = '/path/to/mutation'; + + $mutation = new Mutation( + $originalFilePath = 'path/to/Foo.php', + MutatorName::getName(For_::class), + 10, + $tests = [ + new TestLocation( + 'FooTest::test_it_can_instantiate', + '/path/to/acme/FooTest.php', + 0.01 + ), + ], + $hash, + $mutationFilePath, + 'notCovered#0', + <<<'DIFF' --- Original +++ New @@ @@ @@ -100,8 +88,8 @@ public function test_it_creates_a_process_with_timeout(): void ->method('getMutantCommandLine') ->with( $tests, - $mutantFilePath, - $this->isType('string'), + $mutationFilePath, + $hash, $originalFilePath, $testFrameworkExtraOptions ) @@ -129,7 +117,7 @@ public function test_it_creates_a_process_with_timeout(): void $resultFactoryMock ); - $mutantProcess = $factory->createProcessForMutant($mutant, $testFrameworkExtraOptions); + $mutantProcess = $factory->createProcessForMutation($mutation, $testFrameworkExtraOptions); $process = $mutantProcess->getProcess(); @@ -142,7 +130,7 @@ public function test_it_creates_a_process_with_timeout(): void $this->assertSame(100., $process->getTimeout()); $this->assertFalse($process->isStarted()); - $this->assertSame($mutant, $mutantProcess->getMutant()); + $this->assertSame($mutation, $mutantProcess->getMutation()); $this->assertFalse($mutantProcess->isTimedOut()); $this->assertSame([], $eventDispatcher->getEvents()); diff --git a/tests/phpunit/Process/MutantProcessTest.php b/tests/phpunit/Process/MutantProcessTest.php index 454138ac8..8f7460a26 100644 --- a/tests/phpunit/Process/MutantProcessTest.php +++ b/tests/phpunit/Process/MutantProcessTest.php @@ -35,7 +35,7 @@ namespace Infection\Tests\Process; -use Infection\Mutant\Mutant; +use Infection\Mutation\Mutation; use Infection\Process\MutantProcess; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -49,41 +49,41 @@ final class MutantProcessTest extends TestCase private $processMock; /** - * @var MockObject|Mutant + * @var MockObject|Mutation */ - private $mutantMock; + private $mutationMock; /** * @var MutantProcess */ - private $mutantProcess; + private $mutationProcess; protected function setUp(): void { $this->processMock = $this->createMock(Process::class); - $this->mutantMock = $this->createMock(Mutant::class); + $this->mutationMock = $this->createMock(Mutation::class); - $this->mutantProcess = new MutantProcess($this->processMock, $this->mutantMock); + $this->mutationProcess = new MutantProcess($this->processMock, $this->mutationMock); } public function test_it_exposes_its_state(): void { - $this->assertMutantProcessStateIs( - $this->mutantProcess, + $this->assertMutationProcessStateIs( + $this->mutationProcess, $this->processMock, - $this->mutantMock, + $this->mutationMock, false ); } public function test_it_can_be_marked_as_timed_out(): void { - $this->mutantProcess->markAsTimedOut(); + $this->mutationProcess->markAsTimedOut(); - $this->assertMutantProcessStateIs( - $this->mutantProcess, + $this->assertMutationProcessStateIs( + $this->mutationProcess, $this->processMock, - $this->mutantMock, + $this->mutationMock, true ); } @@ -92,7 +92,7 @@ public function test_it_can_have_a_callback_registered_and_executed(): void { $called = false; - $this->mutantProcess->registerTerminateProcessClosure( + $this->mutationProcess->registerTerminateProcessClosure( static function () use (&$called): void { $called = true; } @@ -100,19 +100,19 @@ static function () use (&$called): void { $this->assertFalse($called); - $this->mutantProcess->terminateProcess(); + $this->mutationProcess->terminateProcess(); $this->assertTrue($called); } - private function assertMutantProcessStateIs( - MutantProcess $mutantProcess, + private function assertMutationProcessStateIs( + MutantProcess $mutationProcess, Process $expectedProcess, - Mutant $expectedMutant, + Mutation $expectedMutation, bool $expectedTimedOut ): void { - $this->assertSame($expectedProcess, $mutantProcess->getProcess()); - $this->assertSame($expectedMutant, $mutantProcess->getMutant()); - $this->assertSame($expectedTimedOut, $mutantProcess->isTimedOut()); + $this->assertSame($expectedProcess, $mutationProcess->getProcess()); + $this->assertSame($expectedMutation, $mutationProcess->getMutation()); + $this->assertSame($expectedTimedOut, $mutationProcess->isTimedOut()); } } diff --git a/tests/phpunit/Process/Runner/MutationTestingRunnerTest.php b/tests/phpunit/Process/Runner/MutationTestingRunnerTest.php index 6713969e1..93b0a1677 100644 --- a/tests/phpunit/Process/Runner/MutationTestingRunnerTest.php +++ b/tests/phpunit/Process/Runner/MutationTestingRunnerTest.php @@ -43,11 +43,8 @@ use Infection\AbstractTestFramework\Coverage\TestLocation; use Infection\Event\MutationTestingWasFinished; use Infection\Event\MutationTestingWasStarted; -use Infection\Mutant\Mutant; -use Infection\Mutant\MutantFactory; use Infection\Mutation\Mutation; use Infection\Mutator\ZeroIteration\For_; -use Infection\PhpParser\MutatedNode; use Infection\Process\Factory\MutantProcessFactory; use Infection\Process\MutantProcess; use Infection\Process\Runner\MutationTestingRunner; @@ -55,12 +52,12 @@ use Infection\Tests\Fixtures\Event\EventDispatcherCollector; use Infection\Tests\Mutator\MutatorName; use Iterator; -use PhpParser\Node\Stmt\Nop; use PHPUnit\Framework\Constraint\Callback; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use function Safe\sprintf; use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Process\Process; /** * @group integration @@ -72,11 +69,6 @@ final class MutationTestingRunnerTest extends TestCase */ private $processFactoryMock; - /** - * @var MutantFactory|MockObject - */ - private $mutantFactoryMock; - /** * @var ProcessRunner|MockObject */ @@ -100,14 +92,12 @@ final class MutationTestingRunnerTest extends TestCase protected function setUp(): void { $this->processFactoryMock = $this->createMock(MutantProcessFactory::class); - $this->mutantFactoryMock = $this->createMock(MutantFactory::class); $this->processRunnerMock = $this->createMock(ProcessRunner::class); $this->eventDispatcher = new EventDispatcherCollector(); $this->fileSystemMock = $this->createMock(Filesystem::class); $this->runner = new MutationTestingRunner( $this->processFactoryMock, - $this->mutantFactoryMock, $this->processRunnerMock, $this->eventDispatcher, $this->fileSystemMock, @@ -145,46 +135,24 @@ public function test_it_applies_and_run_the_mutations(): void ]; $testFrameworkExtraOptions = '--filter=acme/FooTest.php'; - $this->mutantFactoryMock - ->method('create') - ->withConsecutive( - [$mutation0], - [$mutation1] - ) - ->willReturnOnConsecutiveCalls( - $mutant0 = new Mutant( - '/path/to/mutant0', - $mutation0, - 'mutated code 0', - 'diff0' - ), - $mutant1 = new Mutant( - '/path/to/mutant1', - $mutation1, - 'mutated code 1', - 'diff1' - ) - ) - ; - $this->fileSystemMock ->expects($this->exactly(2)) ->method('dumpFile') ->withConsecutive( - ['/path/to/mutant0', 'mutated code 0'], - ['/path/to/mutant1', 'mutated code 1'] + ['/path/to/mutation0.php', 'mutated code 0'], + ['/path/to/mutation1.php', 'mutated code 1'] ) ; $this->processFactoryMock - ->method('createProcessForMutant') + ->method('createProcessForMutation') ->withConsecutive( - [$mutant0, $testFrameworkExtraOptions], - [$mutant1, $testFrameworkExtraOptions] + [$mutation0, $testFrameworkExtraOptions], + [$mutation1, $testFrameworkExtraOptions] ) ->willReturnOnConsecutiveCalls( - $process0 = $this->buildCoveredMutantProcess(), - $process1 = $this->buildCoveredMutantProcess() + $process0 = $this->buildCoveredMutantProcess($mutation0), + $process1 = $this->buildCoveredMutantProcess($mutation1) ) ; @@ -214,58 +182,35 @@ public function test_it_applies_and_run_the_mutations_when_concurrent_execution_ $testFrameworkExtraOptions = '--filter=acme/FooTest.php'; - $this->mutantFactoryMock - ->method('create') - ->withConsecutive( - [$mutation0], - [$mutation1] - ) - ->willReturnOnConsecutiveCalls( - $mutant0 = new Mutant( - '/path/to/mutant0', - $mutation0, - 'mutated code 0', - 'diff0' - ), - $mutant1 = new Mutant( - '/path/to/mutant1', - $mutation1, - 'mutated code 1', - 'diff1' - ) - ) - ; - $this->fileSystemMock ->expects($this->exactly(2)) ->method('dumpFile') ->withConsecutive( - ['/path/to/mutant0', 'mutated code 0'], - ['/path/to/mutant1', 'mutated code 1'] + ['/path/to/mutation0.php', 'mutated code 0'], + ['/path/to/mutation1.php', 'mutated code 1'] ) ; $this->processFactoryMock - ->method('createProcessForMutant') + ->method('createProcessForMutation') ->withConsecutive( - [$mutant0, $testFrameworkExtraOptions], - [$mutant1, $testFrameworkExtraOptions] + [$mutation0, $testFrameworkExtraOptions], + [$mutation1, $testFrameworkExtraOptions] ) ->willReturnOnConsecutiveCalls( - $process0 = $this->buildCoveredMutantProcess(), - $process1 = $this->buildCoveredMutantProcess() + $process0 = $this->buildCoveredMutantProcess($mutation0), + $process1 = $this->buildCoveredMutantProcess($mutation1) ) ; $this->processRunnerMock ->expects($this->once()) ->method('run') - ->with($this->iterableContaining([$process0, $process1]), ) + ->with($this->iterableContaining([$process0, $process1])) ; $this->runner = new MutationTestingRunner( $this->processFactoryMock, - $this->mutantFactoryMock, $this->processRunnerMock, $this->eventDispatcher, $this->fileSystemMock, @@ -291,11 +236,6 @@ public function test_it_passes_through_iterables_when_concurrent_execution_reque ->method($this->anything()) ; - $this->mutantFactoryMock - ->expects($this->never()) - ->method($this->anything()) - ; - $this->processFactoryMock ->expects($this->never()) ->method($this->anything()) @@ -309,7 +249,6 @@ public function test_it_passes_through_iterables_when_concurrent_execution_reque $this->runner = new MutationTestingRunner( $this->processFactoryMock, - $this->mutantFactoryMock, $this->processRunnerMock, $this->eventDispatcher, $this->fileSystemMock, @@ -329,11 +268,6 @@ public function test_it_dispatches_events_even_when_no_mutations_is_given(): voi ->method($this->anything()) ; - $this->mutantFactoryMock - ->expects($this->never()) - ->method($this->anything()) - ; - $this->processRunnerMock ->expects($this->once()) ->method('run') @@ -429,15 +363,15 @@ private function formatExpectedEvents(array $events): string ); } - private function buildCoveredMutantProcess(): MutantProcess + private function buildCoveredMutantProcess(Mutation $mutation): MutantProcess { - $mutantProcess = $this->createMock(MutantProcess::class); - $mutantProcess + $processMock = $this->createMock(Process::class); + $processMock ->expects($this->never()) - ->method('getMutant') + ->method($this->anything()) ; - return $mutantProcess; + return new MutantProcess($processMock, $mutation); } private function someIterable(?callable $callback = null): Callback @@ -479,26 +413,19 @@ private function createMutation(int $i): Mutation { return new Mutation( 'path/to/Foo' . $i . '.php', - [], MutatorName::getName(For_::class), - [ - 'startLine' => $i, - 'endLine' => 15, - 'startTokenPos' => 0, - 'endTokenPos' => 8, - 'startFilePos' => 2, - 'endFilePos' => 4, - ], - 'Unknown', - MutatedNode::wrap(new Nop()), - 0, + 10, [ new TestLocation( 'FooTest::test_it_can_instantiate', '/path/to/acme/FooTest.php', 0.01 ), - ] + ], + 'mutationHash#' . $i, + '/path/to/mutation' . $i . '.php', + 'mutated code ' . $i, + 'diff' ); } } diff --git a/tests/phpunit/TestFramework/TestFrameworkExtraOptionsFilterTest.php b/tests/phpunit/TestFramework/TestFrameworkExtraOptionsFilterTest.php index deb4e8822..480735b43 100644 --- a/tests/phpunit/TestFramework/TestFrameworkExtraOptionsFilterTest.php +++ b/tests/phpunit/TestFramework/TestFrameworkExtraOptionsFilterTest.php @@ -45,9 +45,10 @@ final class TestFrameworkExtraOptionsFilterTest extends TestCase */ public function test_it_skips_filter_for_mutant_process(string $actualExtraOptions, string $expectedExtraOptions): void { - $filter = new TestFrameworkExtraOptionsFilter(); - - $filteredOptions = $filter->filterForMutantProcess($actualExtraOptions, ['--configuration', '--filter', '--testsuite']); + $filteredOptions = (new TestFrameworkExtraOptionsFilter())->filterForMutantProcess( + $actualExtraOptions, + ['--configuration', '--filter', '--testsuite'] + ); $this->assertSame($expectedExtraOptions, $filteredOptions); }