-
Notifications
You must be signed in to change notification settings - Fork 438
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #405 from Landerstraeten/infection-task
Add infection task
- Loading branch information
Showing
9 changed files
with
345 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
# Infection | ||
|
||
Infection is a PHP mutation testing framework based on Abstract Syntax Tree. | ||
|
||
***Composer*** | ||
|
||
``` | ||
composer require --dev infection/infection | ||
``` | ||
|
||
***Config*** | ||
|
||
It lives under the `infection` namespace and has following configurable parameters: | ||
|
||
```yaml | ||
# grumphp.yml | ||
parameters: | ||
tasks: | ||
infection: | ||
configuration: ~ | ||
threads: ~ | ||
test_framework: ~ | ||
only_covered: false | ||
configuration: ~ | ||
min_msi: ~ | ||
min_covered_msi: ~ | ||
mutators: [] | ||
triggered_by: [php] | ||
``` | ||
**configuration** | ||
*Default: null* | ||
By defaut the `infection.json.dist` wil be used. | ||
You can specify an alternate location for this file by changing this option. | ||
|
||
|
||
**threads** | ||
|
||
*Default: null* | ||
|
||
If you want to run tests for mutated code in parallel, set this to something bigger than 1. | ||
It will dramatically speed up the mutation process. | ||
Please note that if your tests somehow depends on each other or use a database, this option can lead to failing tests which give many false-positives results. | ||
|
||
|
||
**test_framework** | ||
|
||
*Default: null* | ||
|
||
This is the name of a test framework to use. Currently Infection supports `PhpUnit` and `PhpSpec`. | ||
|
||
|
||
**only_covered** | ||
|
||
*Default: false* | ||
|
||
Run the mutation testing only for covered by tests files. | ||
|
||
|
||
**configuration** | ||
|
||
*Default: null* | ||
|
||
The path or name to the infection configuration file. | ||
|
||
|
||
**min_msi** | ||
|
||
*Default: null* | ||
|
||
This is a minimum threshold of Mutation Score Indicator (MSI) in percentage. | ||
|
||
|
||
**min_covered_msi** | ||
|
||
*Default: null* | ||
|
||
This is a minimum threshold of Covered Code Mutation Score Indicator (MSI) in percentage. | ||
|
||
|
||
**mutators** | ||
|
||
*Default: []* | ||
|
||
This is a list separated options to specify a particular set of mutators that needs to be executed. | ||
|
||
|
||
**triggered_by** | ||
|
||
*Default: [php]* | ||
|
||
This option will specify which file extensions will trigger the infection task. | ||
By default infection will be triggered by altering a php file. | ||
You can overwrite this option to whatever file you want to use! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
<?php | ||
|
||
namespace spec\GrumPHP\Task; | ||
|
||
use GrumPHP\Collection\FilesCollection; | ||
use GrumPHP\Collection\ProcessArgumentsCollection; | ||
use GrumPHP\Configuration\GrumPHP; | ||
use GrumPHP\Formatter\ProcessFormatterInterface; | ||
use GrumPHP\Process\ProcessBuilder; | ||
use GrumPHP\Runner\TaskResult; | ||
use GrumPHP\Runner\TaskResultInterface; | ||
use GrumPHP\Task\Context\ContextInterface; | ||
use GrumPHP\Task\Context\GitPreCommitContext; | ||
use GrumPHP\Task\Context\RunContext; | ||
use GrumPHP\Task\Infection; | ||
use PhpSpec\ObjectBehavior; | ||
use Symfony\Component\Finder\SplFileInfo; | ||
use Symfony\Component\OptionsResolver\OptionsResolver; | ||
use Symfony\Component\Process\Process; | ||
|
||
class InfectionSpec extends ObjectBehavior | ||
{ | ||
function let(GrumPHP $grumPHP, ProcessBuilder $processBuilder, ProcessFormatterInterface $formatter) | ||
{ | ||
$grumPHP->getTaskConfiguration('infection')->willReturn([]); | ||
$this->beConstructedWith($grumPHP, $processBuilder, $formatter); | ||
} | ||
|
||
function it_is_initializable() | ||
{ | ||
$this->shouldHaveType(Infection::class); | ||
} | ||
|
||
function it_should_have_a_name() | ||
{ | ||
$this->getName()->shouldBe('infection'); | ||
} | ||
|
||
function it_should_have_configurable_options() | ||
{ | ||
$options = $this->getConfigurableOptions(); | ||
$options->shouldBeAnInstanceOf(OptionsResolver::class); | ||
$options->getDefinedOptions()->shouldContain('threads'); | ||
$options->getDefinedOptions()->shouldContain('test_framework'); | ||
$options->getDefinedOptions()->shouldContain('only_covered'); | ||
$options->getDefinedOptions()->shouldContain('configuration'); | ||
$options->getDefinedOptions()->shouldContain('min_msi'); | ||
$options->getDefinedOptions()->shouldContain('min_covered_msi'); | ||
$options->getDefinedOptions()->shouldContain('mutators'); | ||
$options->getDefinedOptions()->shouldContain('triggered_by'); | ||
} | ||
|
||
function it_should_run_in_git_pre_commit_context(GitPreCommitContext $context) | ||
{ | ||
$this->canRunInContext($context)->shouldReturn(true); | ||
} | ||
|
||
function it_should_run_in_run_context(RunContext $context) | ||
{ | ||
$this->canRunInContext($context)->shouldReturn(true); | ||
} | ||
|
||
function it_does_not_do_anything_if_there_are_no_files(ProcessBuilder $processBuilder, ContextInterface $context) | ||
{ | ||
$processBuilder->buildProcess('infection')->shouldNotBeCalled(); | ||
$processBuilder->buildProcess()->shouldNotBeCalled(); | ||
$context->getFiles()->willReturn(new FilesCollection()); | ||
|
||
$result = $this->run($context); | ||
$result->shouldBeAnInstanceOf(TaskResultInterface::class); | ||
$result->getResultCode()->shouldBe(TaskResult::SKIPPED); | ||
} | ||
|
||
function it_runs_the_suite(ProcessBuilder $processBuilder, Process $process, ContextInterface $context) | ||
{ | ||
$arguments = new ProcessArgumentsCollection(); | ||
$processBuilder->createArgumentsForCommand('infection')->willReturn($arguments); | ||
$processBuilder->buildProcess($arguments)->willReturn($process); | ||
|
||
$process->run()->shouldBeCalled(); | ||
$process->isSuccessful()->willReturn(true); | ||
$process->getErrorOutput()->willReturn(''); | ||
$process->getOutput()->willReturn(''); | ||
|
||
$context->getFiles()->willReturn(new FilesCollection([ | ||
new SplFileInfo('test.php', '.', 'test.php') | ||
])); | ||
|
||
$result = $this->run($context); | ||
$result->shouldBeAnInstanceOf(TaskResultInterface::class); | ||
$result->isPassed()->shouldBe(true); | ||
} | ||
|
||
function it_throws_exception_if_the_process_fails(ProcessBuilder $processBuilder, Process $process, ContextInterface $context) | ||
{ | ||
$arguments = new ProcessArgumentsCollection(); | ||
$processBuilder->createArgumentsForCommand('infection')->willReturn($arguments); | ||
$processBuilder->buildProcess($arguments)->willReturn($process); | ||
|
||
$process->run()->shouldBeCalled(); | ||
$process->isSuccessful()->willReturn(false); | ||
|
||
$context->getFiles()->willReturn(new FilesCollection([ | ||
new SplFileInfo('src/Collection/TaskResultCollection.php', 'src/Collection', 'TaskResultCollection.php'), | ||
])); | ||
|
||
$result = $this->run($context); | ||
$result->shouldBeAnInstanceOf(TaskResultInterface::class); | ||
$result->isPassed()->shouldBe(false); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
<?php | ||
|
||
namespace GrumPHP\Task; | ||
|
||
use GrumPHP\Runner\TaskResult; | ||
use GrumPHP\Task\Context\ContextInterface; | ||
use GrumPHP\Task\Context\GitPreCommitContext; | ||
use GrumPHP\Task\Context\RunContext; | ||
use Symfony\Component\OptionsResolver\OptionsResolver; | ||
|
||
/** | ||
* Infection task | ||
*/ | ||
class Infection extends AbstractExternalTask | ||
{ | ||
/** | ||
* @return string | ||
*/ | ||
public function getName() | ||
{ | ||
return 'infection'; | ||
} | ||
|
||
/** | ||
* @return OptionsResolver | ||
*/ | ||
public function getConfigurableOptions() | ||
{ | ||
$resolver = new OptionsResolver(); | ||
|
||
$resolver->setDefaults([ | ||
'threads' => null, | ||
'test_framework' => null, | ||
'only_covered' => false, | ||
'configuration' => null, | ||
'min_msi' => null, | ||
'min_covered_msi' => null, | ||
'mutators' => [], | ||
'triggered_by' => ['php'], | ||
]); | ||
|
||
$resolver->addAllowedTypes('threads', ['null', 'int']); | ||
$resolver->addAllowedTypes('test_framework', ['null', 'string']); | ||
$resolver->addAllowedTypes('only_covered', ['bool']); | ||
$resolver->addAllowedTypes('configuration', ['null', 'string']); | ||
$resolver->addAllowedTypes('min_msi', ['null', 'integer']); | ||
$resolver->addAllowedTypes('min_covered_msi', ['null', 'integer']); | ||
$resolver->addAllowedTypes('mutators', ['array']); | ||
$resolver->addAllowedTypes('triggered_by', ['array']); | ||
|
||
return $resolver; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function canRunInContext(ContextInterface $context) | ||
{ | ||
return ($context instanceof GitPreCommitContext || $context instanceof RunContext); | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function run(ContextInterface $context) | ||
{ | ||
$config = $this->getConfiguration(); | ||
$files = $context->getFiles()->extensions($config['triggered_by']); | ||
|
||
if (0 === count($files)) { | ||
return TaskResult::createSkipped($this, $context); | ||
} | ||
|
||
$arguments = $this->processBuilder->createArgumentsForCommand('infection'); | ||
$arguments->add('--no-interaction'); | ||
$arguments->addOptionalArgument('--threads=%s', $config['threads']); | ||
$arguments->addOptionalArgument('--test-framework=%s', $config['test_framework']); | ||
$arguments->addOptionalArgument('--only-covered', $config['only_covered']); | ||
$arguments->addOptionalArgument('--configuration=%s', $config['configuration']); | ||
$arguments->addOptionalArgument('--min-msi=%s', $config['min_msi']); | ||
$arguments->addOptionalArgument('--min-covered-msi=%s', $config['min_covered_msi']); | ||
$arguments->addOptionalCommaSeparatedArgument('--mutators=%s', $config['mutators']); | ||
|
||
if ($context instanceof GitPreCommitContext) { | ||
$arguments->addArgumentWithCommaSeparatedFiles('--filter=%s', $files); | ||
} | ||
|
||
$process = $this->processBuilder->buildProcess($arguments); | ||
$process->run(); | ||
|
||
if (!$process->isSuccessful()) { | ||
return TaskResult::createFailed($this, $context, $this->formatter->format($process)); | ||
} | ||
|
||
return TaskResult::createPassed($this, $context); | ||
} | ||
} |