Skip to content

Commit

Permalink
Introduce --noop option to run Noop mutators that does not change t…
Browse files Browse the repository at this point in the history
…he source code (AST) (#1465)

* Introduce `--noop` option to run noop mutators that does not change the source code (AST)

This is needed for debugging purposes, to understand how and why Infection kills mutant even if no change is made for the given source code line.

If, using `--noop` options, we see killed mutants, it means that Infection environment somehow breaks the tests

* Use intersection of types for mocks

* Remove extra dot in the option description
  • Loading branch information
maks-rafalko committed Jan 14, 2021
1 parent ab7b999 commit d133849
Show file tree
Hide file tree
Showing 13 changed files with 313 additions and 32 deletions.
11 changes: 10 additions & 1 deletion src/Command/RunCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ final class RunCommand extends BaseCommand
/** @var string */
private const OPTION_LOGGER_GITHUB = 'logger-github';

private const OPTION_USE_NOOP_MUTATORS = 'noop';

/** @var string */
private const OPTION_MIN_MSI = 'min-msi';

Expand Down Expand Up @@ -249,6 +251,12 @@ protected function configure(): void
InputOption::VALUE_NONE,
'Log escaped Mutants as GitHub Annotations.',
)
->addOption(
self::OPTION_USE_NOOP_MUTATORS,
null,
InputOption::VALUE_NONE,
'Use noop mutators that do not change AST. For debugging purposes.',
)
->addOption(
self::OPTION_MIN_MSI,
null,
Expand Down Expand Up @@ -435,7 +443,8 @@ private function createContainer(IO $io, LoggerInterface $logger): Container
(bool) $input->getOption(self::OPTION_DRY_RUN),
$gitDiffFilter,
$gitDiffBase,
(bool) $input->getOption(self::OPTION_LOGGER_GITHUB)
(bool) $input->getOption(self::OPTION_LOGGER_GITHUB),
(bool) $input->getOption(self::OPTION_USE_NOOP_MUTATORS)
);
}

Expand Down
5 changes: 3 additions & 2 deletions src/Configuration/ConfigurationFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ public function create(
bool $dryRun,
?string $gitDiffFilter,
?string $gitDiffBase,
bool $useGitHubLogger
bool $useGitHubLogger,
bool $useNoopMutators
): Configuration {
$configDir = dirname($schema->getFile());

Expand All @@ -135,7 +136,7 @@ public function create(

$resolvedMutatorsArray = $this->resolveMutators($schema->getMutators(), $mutatorsInput);

$mutators = $this->mutatorFactory->create($resolvedMutatorsArray);
$mutators = $this->mutatorFactory->create($resolvedMutatorsArray, $useNoopMutators);
$ignoreSourceCodeMutatorsMap = $this->retrieveIgnoreSourceCodeMutatorsMap($resolvedMutatorsArray);

return new Configuration(
Expand Down
13 changes: 9 additions & 4 deletions src/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ final class Container
public const DEFAULT_GIT_DIFF_FILTER = null;
public const DEFAULT_GIT_DIFF_BASE = null;
public const DEFAULT_USE_GITHUB_LOGGER = false;
public const DEFAULT_USE_NOOP_MUTATORS = false;
public const DEFAULT_NO_PROGRESS = false;
public const DEFAULT_FORCE_PROGRESS = false;
public const DEFAULT_EXISTING_COVERAGE_PATH = null;
Expand Down Expand Up @@ -682,7 +683,8 @@ public static function create(): self
self::DEFAULT_DRY_RUN,
self::DEFAULT_GIT_DIFF_FILTER,
self::DEFAULT_GIT_DIFF_BASE,
self::DEFAULT_USE_GITHUB_LOGGER
self::DEFAULT_USE_GITHUB_LOGGER,
self::DEFAULT_USE_NOOP_MUTATORS
);
}

Expand Down Expand Up @@ -712,7 +714,8 @@ public function withValues(
bool $dryRun,
?string $gitDiffFilter,
?string $gitDiffBase,
bool $useGitHubLogger
bool $useGitHubLogger,
bool $useNoopMutators
): self {
$clone = clone $this;

Expand Down Expand Up @@ -786,7 +789,8 @@ static function (self $container) use (
$dryRun,
$gitDiffFilter,
$gitDiffBase,
$useGitHubLogger
$useGitHubLogger,
$useNoopMutators
): Configuration {
return $container->getConfigurationFactory()->create(
$container->getSchemaConfiguration(),
Expand All @@ -810,7 +814,8 @@ static function (self $container) use (
$dryRun,
$gitDiffFilter,
$gitDiffBase,
$useGitHubLogger
$useGitHubLogger,
$useNoopMutators
);
}
);
Expand Down
15 changes: 7 additions & 8 deletions src/Mutator/MutatorFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ final class MutatorFactory
*
* @return array<string, Mutator<\PhpParser\Node>>
*/
public function create(array $resolvedMutators): array
public function create(array $resolvedMutators, bool $useNoopMutators): array
{
$mutators = [];

Expand Down Expand Up @@ -82,16 +82,15 @@ public function create(array $resolvedMutators): array
self::getConfigurableMutator($mutatorClassName, $settings) :
new $mutatorClassName();

if ($ignored === []) {
$mutators[$mutator->getName()] = $mutator;
if ($ignored !== []) {
$mutator = new IgnoreMutator(new IgnoreConfig($ignored), $mutator);
}

continue;
if ($useNoopMutators) {
$mutator = new NoopMutator($mutator);
}

$mutators[$mutator->getName()] = new IgnoreMutator(
new IgnoreConfig($ignored),
$mutator
);
$mutators[$mutator->getName()] = $mutator;
}

return $mutators;
Expand Down
88 changes: 88 additions & 0 deletions src/Mutator/NoopMutator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?php
/**
* This code is licensed under the BSD 3-Clause License.
*
* Copyright (c) 2017, Maks Rafalko
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

declare(strict_types=1);

namespace Infection\Mutator;

use DomainException;
use PhpParser\Node;
use function Safe\sprintf;

/**
* @internal
*
* @template TNode of Node
* @implements Mutator<TNode>
*/
final class NoopMutator implements Mutator
{
/** @var Mutator<TNode> */
private Mutator $mutator;

/**
* @param Mutator<TNode> $mutator
*/
public function __construct(Mutator $mutator)
{
$this->mutator = $mutator;
}

public static function getDefinition(): ?Definition
{
throw new DomainException(sprintf(
'The class "%s" does not have a definition',
self::class
));
}

public function canMutate(Node $node): bool
{
return $this->mutator->canMutate($node);
}

/**
* @psalm-mutation-free
*
* @return iterable<Node|Node[]>
*/
public function mutate(Node $node): iterable
{
yield $node;
}

public function getName(): string
{
return $this->mutator->getName();
}
}

0 comments on commit d133849

Please sign in to comment.