Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exclude mutations that are over specified time limit #1171

Merged
merged 23 commits into from Jul 8, 2020
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
edcac2e
Filter out mutations are likely to be over our time limit
sanmai Mar 15, 2020
b0f353d
Merge branch 'master' into pr/2020-03/relative-timeout
sanmai Mar 16, 2020
f2b56cb
Optimize JUnit test lookups to stop on the first element
sanmai Mar 16, 2020
5be4620
Merge branch 'master' into pr/2020-03/relative-timeout
sanmai Mar 24, 2020
4ef9754
Merge branch 'pr/2020-03/relative-timeout' of github.com:sanmai/infec…
sanmai Mar 24, 2020
857a25e
Merge branch 'master' into pr/2020-03/relative-timeout
sanmai Mar 24, 2020
08b2f61
Merge branch 'master' into pr/2020-03/relative-timeout
sanmai Mar 25, 2020
81e1a4c
Log constrained
sanmai Mar 25, 2020
4a131a9
Add final report line
sanmai Mar 25, 2020
7870590
Address review comment
sanmai Mar 27, 2020
32433ed
Use S for skipped
sanmai Mar 30, 2020
9eaf9a4
Update MutationTestingRunnerTest
sanmai Mar 30, 2020
7695061
Update MutationTestingRunnerTest
sanmai Mar 30, 2020
15b7d2c
Merge branch 'master' into pr/2020-03/relative-timeout
sanmai Mar 31, 2020
35e012b
s/getTimeToTest/getNominalTestExecutionTime/g
sanmai Mar 30, 2020
9d07a64
Merge branch 'master' into pr/2020-03/relative-timeout
sanmai Jul 5, 2020
226aa54
Fix MutationTestingRunnerTest
sanmai Jul 5, 2020
f99f565
Fix tests
sanmai Jul 6, 2020
9c5b16f
Fix E2E tests
sanmai Jul 6, 2020
9a16224
Fix remaining E2E test
sanmai Jul 6, 2020
ba87ee0
Add OutputFormatterStyleConfiguratorTest
sanmai Jul 6, 2020
d4d5810
Merge branch 'master' into pr/2020-03/relative-timeout
sanmai Jul 7, 2020
cf7bbaf
Update tests/phpunit/Console/OutputFormatterStyleConfiguratorTest.php
sanmai Jul 8, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/Console/OutputFormatter/DotFormatter.php
Expand Up @@ -66,7 +66,8 @@ public function start(int $mutationCount): void
. '<escaped>M</escaped>: escaped, '
. '<uncovered>U</uncovered>: uncovered, '
. '<with-error>E</with-error>: fatal error, '
. '<timeout>T</timeout>: timed out',
. '<timeout>T</timeout>: timed out, '
. '<skipped>S</skipped>: skipped',
'',
]);
}
Expand All @@ -91,6 +92,10 @@ public function advance(MutantExecutionResult $executionResult, int $mutationCou
case DetectionStatus::TIMED_OUT:
$this->output->write('<timeout>T</timeout>');

break;
case DetectionStatus::SKIPPED:
$this->output->write('<skipped>S</skipped>');

break;
case DetectionStatus::ERROR:
$this->output->write('<with-error>E</with-error>');
Expand Down
1 change: 1 addition & 0 deletions src/Console/OutputFormatterStyleConfigurator.php
Expand Up @@ -69,6 +69,7 @@ private static function configureMutantStyle(OutputFormatterInterface $formatter
new OutputFormatterStyle('red', null, ['bold'])
);
$formatter->setStyle('killed', new OutputFormatterStyle('green'));
$formatter->setStyle('skipped', new OutputFormatterStyle('magenta'));
$formatter->setStyle('code', new OutputFormatterStyle('white'));
}

Expand Down
3 changes: 2 additions & 1 deletion src/Container.php
Expand Up @@ -565,7 +565,8 @@ public static function create(): self
$container->getConfiguration()->isDryRun()
? new DummyFileSystem()
: $container->getFileSystem(),
$container->getConfiguration()->noProgress()
$container->getConfiguration()->noProgress(),
$container->getConfiguration()->getProcessTimeout()
);
},
LineRangeCalculator::class => static function (): LineRangeCalculator {
Expand Down
3 changes: 2 additions & 1 deletion src/Event/MutantProcessWasFinished.php
Expand Up @@ -39,8 +39,9 @@

/**
* @internal
* @final
*/
final class MutantProcessWasFinished
class MutantProcessWasFinished
{
private $executionResult;

Expand Down
Expand Up @@ -151,6 +151,7 @@ private function showMetrics(): void
$this->output->writeln('<options=bold>' . $this->getPadded($this->metricsCalculator->getEscapedCount()) . '</options=bold> covered mutants were not detected');
$this->output->writeln('<options=bold>' . $this->getPadded($this->metricsCalculator->getErrorCount()) . '</options=bold> errors were encountered');
$this->output->writeln('<options=bold>' . $this->getPadded($this->metricsCalculator->getTimedOutCount()) . '</options=bold> time outs were encountered');
$this->output->writeln('<options=bold>' . $this->getPadded($this->metricsCalculator->getSkippedCount()) . '</options=bold> mutants required more time than configured');

$mutationScoreIndicator = floor($this->metricsCalculator->getMutationScoreIndicator());
$msiTag = $this->getPercentageTag($mutationScoreIndicator);
Expand Down
5 changes: 5 additions & 0 deletions src/Logger/DebugFileLogger.php
Expand Up @@ -88,6 +88,11 @@ public function getLogLines(): array
'Timed Out',
$separateSections
);
$logs[] = $this->getResultsLine(
$this->metricsCalculator->getSkippedExecutionResults(),
'Skipped',
$separateSections
);

if (!$this->onlyCoveredMode) {
$logs[] = $this->getResultsLine(
Expand Down
1 change: 1 addition & 0 deletions src/Logger/JsonLogger.php
Expand Up @@ -69,6 +69,7 @@ public function getLogLines(): array
'notCoveredCount' => $this->metricsCalculator->getNotTestedCount(),
'escapedCount' => $this->metricsCalculator->getEscapedCount(),
'errorCount' => $this->metricsCalculator->getErrorCount(),
'skippedCount' => $this->metricsCalculator->getSkippedCount(),
'timeOutCount' => $this->metricsCalculator->getTimedOutCount(),
'msi' => $this->metricsCalculator->getMutationScoreIndicator(),
'mutationCodeCoverage' => $this->metricsCalculator->getCoverageRate(),
Expand Down
3 changes: 2 additions & 1 deletion src/Logger/PerMutatorLogger.php
Expand Up @@ -68,7 +68,7 @@ public function getLogLines(): array
$calculatorPerMutator = $this->createMetricsPerMutators();

$table = [
['Mutator', 'Mutations', 'Killed', 'Escaped', 'Errors', 'Timed Out', 'MSI (%s)', 'Covered MSI (%s)'],
['Mutator', 'Mutations', 'Killed', 'Escaped', 'Errors', 'Timed Out', 'Skipped', 'MSI (%s)', 'Covered MSI (%s)'],
];

foreach ($calculatorPerMutator as $mutatorName => $calculator) {
Expand All @@ -81,6 +81,7 @@ public function getLogLines(): array
(string) $calculator->getEscapedCount(),
(string) $calculator->getErrorCount(),
(string) $calculator->getTimedOutCount(),
(string) $calculator->getSkippedCount(),
self::formatScore($calculator->getMutationScoreIndicator()),
self::formatScore($calculator->getCoveredCodeMutationScoreIndicator()),
];
Expand Down
1 change: 1 addition & 0 deletions src/Logger/SummaryFileLogger.php
Expand Up @@ -61,6 +61,7 @@ public function getLogLines(): array
'Errored: ' . $this->metricsCalculator->getErrorCount(),
'Escaped: ' . $this->metricsCalculator->getEscapedCount(),
'Timed Out: ' . $this->metricsCalculator->getTimedOutCount(),
'Skipped: ' . $this->metricsCalculator->getSkippedCount(),
'Not Covered: ' . $this->metricsCalculator->getNotTestedCount(),
'',
];
Expand Down
8 changes: 8 additions & 0 deletions src/Logger/TextFileLogger.php
Expand Up @@ -77,18 +77,26 @@ public function getLogLines(): array
'Escaped',
$separateSections
);

$logs[] = $this->getResultsLine(
$this->metricsCalculator->getTimedOutExecutionResults(),
'Timed Out',
$separateSections
);

$logs[] = $this->getResultsLine(
$this->metricsCalculator->getSkippedExecutionResults(),
'Skipped',
$separateSections
);

if ($this->debugVerbosity) {
$logs[] = $this->getResultsLine(
$this->metricsCalculator->getKilledExecutionResults(),
'Killed',
$separateSections
);

$logs[] = $this->getResultsLine(
$this->metricsCalculator->getErrorExecutionResults(),
'Errors',
Expand Down
2 changes: 1 addition & 1 deletion src/Metrics/Calculator.php
Expand Up @@ -89,7 +89,7 @@ public static function fromMetrics(MetricsCalculator $calculator): self
$calculator->getErrorCount(),
$calculator->getTimedOutCount(),
$calculator->getNotTestedCount(),
$calculator->getTotalMutantsCount()
$calculator->getTestedMutantsCount()
);
}

Expand Down
31 changes: 31 additions & 0 deletions src/Metrics/MetricsCalculator.php
Expand Up @@ -51,6 +51,7 @@ class MetricsCalculator
private $errorExecutionResults;
private $escapedExecutionResults;
private $timedOutExecutionResults;
private $skippedExecutionResults;
private $notCoveredExecutionResults;
private $allExecutionResults;

Expand All @@ -64,6 +65,11 @@ class MetricsCalculator
*/
private $errorCount = 0;

/**
* @var int
*/
private $skippedCount = 0;

/**
* @var int
*/
Expand Down Expand Up @@ -96,6 +102,7 @@ public function __construct(int $roundingPrecision)
$this->errorExecutionResults = new SortableMutantExecutionResults();
$this->escapedExecutionResults = new SortableMutantExecutionResults();
$this->timedOutExecutionResults = new SortableMutantExecutionResults();
$this->skippedExecutionResults = new SortableMutantExecutionResults();
$this->notCoveredExecutionResults = new SortableMutantExecutionResults();
$this->allExecutionResults = new SortableMutantExecutionResults();
}
Expand Down Expand Up @@ -136,6 +143,12 @@ public function collect(MutantExecutionResult ...$executionResults): void

break;

case DetectionStatus::SKIPPED:
$this->skippedCount++;
$this->skippedExecutionResults->add($executionResult);

break;

case DetectionStatus::ERROR:
$this->errorCount++;
$this->errorExecutionResults->add($executionResult);
Expand Down Expand Up @@ -172,6 +185,14 @@ public function getErrorExecutionResults(): array
return $this->errorExecutionResults->getSortedExecutionResults();
}

/**
* @return MutantExecutionResult[]
*/
public function getSkippedExecutionResults(): array
{
return $this->skippedExecutionResults->getSortedExecutionResults();
}

/**
* @return MutantExecutionResult[]
*/
Expand Down Expand Up @@ -214,6 +235,11 @@ public function getErrorCount(): int
return $this->errorCount;
}

public function getSkippedCount(): int
{
return $this->skippedCount;
}

public function getEscapedCount(): int
{
return $this->escapedCount;
Expand All @@ -234,6 +260,11 @@ public function getTotalMutantsCount(): int
return $this->totalMutantsCount;
}

public function getTestedMutantsCount(): int
{
return $this->totalMutantsCount - $this->skippedCount;
}

/**
* Mutation Score Indicator (MSI)
*/
Expand Down
2 changes: 2 additions & 0 deletions src/Mutant/DetectionStatus.php
Expand Up @@ -48,13 +48,15 @@ final class DetectionStatus
public const ESCAPED = 'escaped';
public const ERROR = 'error';
public const TIMED_OUT = 'timed out';
public const SKIPPED = 'skipped';
public const NOT_COVERED = 'not covered';

public const ALL = [
self::KILLED,
self::ESCAPED,
self::ERROR,
self::TIMED_OUT,
self::SKIPPED,
self::NOT_COVERED,
];
}
34 changes: 22 additions & 12 deletions src/Mutant/MutantExecutionResult.php
Expand Up @@ -82,19 +82,12 @@ public function __construct(

public static function createFromNonCoveredMutant(Mutant $mutant): self
{
$mutation = $mutant->getMutation();
return self::createFromMutant($mutant, DetectionStatus::NOT_COVERED);
}

return new self(
'',
'',
DetectionStatus::NOT_COVERED,
$mutant->getDiff(),
$mutant->getMutation()->getMutatorName(),
$mutation->getOriginalFilePath(),
$mutation->getOriginalStartingLine(),
$mutant->getPrettyPrintedOriginalCode(),
$mutant->getMutatedCode()
);
public static function createFromTimeSkippedMutant(Mutant $mutant): self
{
return self::createFromMutant($mutant, DetectionStatus::SKIPPED);
}

public function getProcessCommandLine(): string
Expand Down Expand Up @@ -141,4 +134,21 @@ public function getMutatedCode(): string
{
return $this->mutatedCode;
}

private static function createFromMutant(Mutant $mutant, string $detectionStatus): self
{
$mutation = $mutant->getMutation();

return new self(
'',
'',
$detectionStatus,
$mutant->getDiff(),
$mutant->getMutation()->getMutatorName(),
$mutation->getOriginalFilePath(),
$mutation->getOriginalStartingLine(),
$mutant->getPrettyPrintedOriginalCode(),
$mutant->getMutatedCode()
);
}
}
22 changes: 20 additions & 2 deletions src/Mutation/Mutation.php
Expand Up @@ -37,7 +37,8 @@

use function array_intersect_key;
use function array_keys;
use function count;
use function array_map;
use function array_sum;
use function implode;
use Infection\AbstractTestFramework\Coverage\TestLocation;
use Infection\Mutator\ProfileList;
Expand All @@ -62,6 +63,10 @@ class Mutation
private $originalFileAst;
private $tests;
private $coveredByTests;
/**
* @var float|null
*/
private $nominalTimeToTest;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the unit for this time, seconds, milliseconds, etc?

Suggest:

  1. update name to nominalTimeInSecondsToTest or whatever unit actually is.
  2. or better still create a value object Seconds, 'MiliSeconds` to aid clarity here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's all seconds here.


/**
* @var string|null
Expand Down Expand Up @@ -97,7 +102,7 @@ public function __construct(
$this->mutatedNode = $mutatedNode;
$this->mutationByMutatorIndex = $mutationByMutatorIndex;
$this->tests = $tests;
$this->coveredByTests = count($tests) > 0;
$this->coveredByTests = $tests !== [];
}

public function getOriginalFilePath(): string
Expand Down Expand Up @@ -155,6 +160,19 @@ public function getAllTests(): array
return $this->tests;
}

/**
* Overall time needed to run known tests for a mutation, excluding dependencies.
*/
public function getNominalTestExecutionTime(): float
{
return $this->nominalTimeToTest ?? $this->nominalTimeToTest = array_sum(array_map(
static function (TestLocation $testLocation) {
return $testLocation->getExecutionTime();
},
$this->tests
));
}

public function getHash(): string
{
return $this->hash ?? $this->hash = $this->createHash();
Expand Down
4 changes: 2 additions & 2 deletions src/Process/Factory/MutantProcessFactory.php
Expand Up @@ -58,7 +58,7 @@ class MutantProcessFactory
// TODO: is it necessary for the timeout to be an int?
public function __construct(
TestFrameworkAdapter $testFrameworkAdapter,
int $timeout,
float $timeout,
EventDispatcher $eventDispatcher,
MutantExecutionResultFactory $resultFactory
) {
Expand All @@ -80,7 +80,7 @@ public function createProcessForMutant(Mutant $mutant, string $testFrameworkExtr
)
);

$process->setTimeout((float) $this->timeout);
$process->setTimeout($this->timeout);

if (method_exists($process, 'inheritEnvironmentVariables')) {
// in version 4.4.0 this method is deprecated and removed in 5.0.0
Expand Down
16 changes: 15 additions & 1 deletion src/Process/Runner/MutationTestingRunner.php
Expand Up @@ -59,21 +59,24 @@ final class MutationTestingRunner
private $eventDispatcher;
private $fileSystem;
private $runConcurrently;
private $timeout;

public function __construct(
MutantProcessFactory $processFactory,
MutantFactory $mutantFactory,
ProcessRunner $processRunner,
EventDispatcher $eventDispatcher,
Filesystem $fileSystem,
bool $runConcurrently
bool $runConcurrently,
float $timeout
) {
$this->processFactory = $processFactory;
$this->mutantFactory = $mutantFactory;
$this->processRunner = $processRunner;
$this->eventDispatcher = $eventDispatcher;
$this->fileSystem = $fileSystem;
$this->runConcurrently = $runConcurrently;
$this->timeout = $timeout;
}

/**
Expand All @@ -100,6 +103,17 @@ public function run(iterable $mutations, string $testFrameworkExtraOptions): voi

return false;
})
->filter(function (Mutant $mutant) {
theofidry marked this conversation as resolved.
Show resolved Hide resolved
if ($mutant->getMutation()->getNominalTestExecutionTime() < $this->timeout) {
return true;
}

$this->eventDispatcher->dispatch(new MutantProcessWasFinished(
MutantExecutionResult::createFromTimeSkippedMutant($mutant)
));

return false;
})
->map(function (Mutant $mutant) use ($testFrameworkExtraOptions): ProcessBearer {
$this->fileSystem->dumpFile($mutant->getFilePath(), $mutant->getMutatedCode());

Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/Config_Bootstrap/expected-file.txt
@@ -1 +1 @@
Hello World!
Hello World!