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

Add notice to console output if actual msi is higher than required msi #877

Merged
merged 28 commits into from
Dec 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
1a83276
Add notice to console output if actual msi is higher than require msi…
Oct 29, 2019
31c408b
Fix code style #811
Oct 29, 2019
2698403
Merge branch 'master' into master
LeoVie Nov 19, 2019
1b46e52
Remove condition which returns false always #811
LeoVie Nov 20, 2019
5085ae2
Add test for new console output method #811
LeoVie Nov 20, 2019
05bbc74
Add test for new method in TestRunConstraintChecker #811
LeoVie Nov 20, 2019
b68828d
Fix code style #811
LeoVie Nov 20, 2019
51be9a7
Fix code style #811
LeoVie Nov 20, 2019
1ada401
Merge branch 'master' of https://github.com/LeoVie/infection
LeoVie Nov 20, 2019
ca36b5d
Merge branch 'master' into master
LeoVie Nov 20, 2019
97ba056
Improve code style #811
LeoVie Nov 21, 2019
7588da9
Merge branch 'master' of https://github.com/LeoVie/infection
LeoVie Nov 21, 2019
00e1cc5
Merge branch 'master' into master
LeoVie Nov 22, 2019
9625817
Merge branch 'master' into master
LeoVie Nov 23, 2019
5d8a62a
Merge branch 'master' into master
maks-rafalko Nov 23, 2019
3ddb759
Merge branch 'master' into master
LeoVie Nov 25, 2019
83cec69
Replace foreach loop by method call to make sure the constraint check…
LeoVie Nov 28, 2019
550efad
Use updated constant names #811
LeoVie Nov 28, 2019
af0d2ab
Add actualOverRequiredType #811
LeoVie Nov 28, 2019
e89d920
Use updated constant names #811
LeoVie Nov 28, 2019
b201600
Merge branch 'master' of https://github.com/LeoVie/infection
LeoVie Nov 28, 2019
68ecc78
Merge branch 'master' into master
LeoVie Nov 28, 2019
32f4ab2
Remove passing default parameter value #811
LeoVie Nov 28, 2019
20e4fe9
Change string concatenation to use sprintf #811
LeoVie Nov 28, 2019
280f56c
Fix calls to removed method in test #811
LeoVie Nov 28, 2019
aa5fcbc
Merge branch 'master' into master
maks-rafalko Nov 30, 2019
c3b02f3
Merge remote-tracking branch 'upstream/master'
theofidry Dec 4, 2019
37541e3
Fix CS
theofidry Dec 4, 2019
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
8 changes: 8 additions & 0 deletions src/Command/InfectionCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,14 @@ private function checkMetrics(): bool
return false;
}

if ($constraintChecker->isActualOverRequired()) {
$this->consoleOutput->logMinMsiCanGetIncreasedNotice(
$this->container['metrics'],
$constraintChecker->getMinRequiredValue(),
$constraintChecker->getActualOverRequiredType()
);
}

$this->eventDispatcher->dispatch(new ApplicationExecutionFinished());

return true;
Expand Down
32 changes: 32 additions & 0 deletions src/Console/ConsoleOutput.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@

namespace Infection\Console;

use Infection\Exception\InvalidTypeException;
use Infection\Mutant\Exception\MsiCalculationException;
use Infection\Mutant\MetricsCalculator;
use Infection\Process\Runner\TestRunConstraintChecker;
Expand All @@ -47,6 +48,7 @@ final class ConsoleOutput
{
private const RUNNING_WITH_DEBUGGER_NOTE = 'You are running Infection with %s enabled.';
private const CI_FLAG_ERROR = 'The minimum required %s percentage should be %s%%, but actual is %s%%. Improve your tests!';
private const MIN_MSI_CAN_GET_INCREASED_NOTICE = 'The %s is %s%% percent points over the required %s. Consider increasing the required %s percentage the next time you run infection.';

/**
* @var SymfonyStyle
Expand Down Expand Up @@ -87,6 +89,36 @@ public function logBadMsiErrorMessage(MetricsCalculator $metricsCalculator, floa
);
}

/**
* @throws InvalidTypeException
*/
public function logMinMsiCanGetIncreasedNotice(MetricsCalculator $metricsCalculator, float $minMsi, string $type): void
{
if ($type !== TestRunConstraintChecker::MSI_OVER_MIN_MSI && $type !== TestRunConstraintChecker::COVERED_MSI_OVER_MIN_MSI) {
throw InvalidTypeException::create($type);
}

if ($type === TestRunConstraintChecker::MSI_OVER_MIN_MSI) {
$typeString = 'MSI';
$msi = $metricsCalculator->getMutationScoreIndicator();
} else {
$typeString = 'Covered Code MSI';
$msi = $metricsCalculator->getCoveredCodeMutationScoreIndicator();
}

$msiDifference = $msi - $minMsi;

$this->io->note(
sprintf(
self::MIN_MSI_CAN_GET_INCREASED_NOTICE,
$typeString,
$msiDifference,
$typeString,
$typeString
)
);
}

public function logRunningWithDebugger(string $debugger): void
{
$this->io->writeln(sprintf(self::RUNNING_WITH_DEBUGGER_NOTE, $debugger));
Expand Down
51 changes: 51 additions & 0 deletions src/Exception/InvalidTypeException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?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\Exception;

use Exception;

/**
* @internal
*/
final class InvalidTypeException extends Exception
{
public static function create(string $type): self
{
return new self(
sprintf('Invalid type "%s" passed.', $type)
);
}
}
55 changes: 54 additions & 1 deletion src/Process/Runner/TestRunConstraintChecker.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ final class TestRunConstraintChecker

public const COVERED_MSI_FAILURE = 'min-covered-msi';

public const MSI_OVER_MIN_MSI = 'msi-over-min-msi';

public const COVERED_MSI_OVER_MIN_MSI = 'covered-msi-over-min-msi';

private const VALUE_OVER_REQUIRED_TOLERANCE = 0.1;

/**
* @var MetricsCalculator
*/
Expand All @@ -71,6 +77,11 @@ final class TestRunConstraintChecker
*/
private $failureType = '';

/**
* @var string
*/
private $actualOverRequiredType = '';

public function __construct(
MetricsCalculator $metricsCalculator,
bool $ignoreMsiWithNoMutations,
Expand Down Expand Up @@ -104,14 +115,38 @@ public function hasTestRunPassedConstraints(): bool
return true;
}

public function isActualOverRequired(): bool
{
if ($this->hasMsiOverRequired()) {
$this->actualOverRequiredType = self::MSI_OVER_MIN_MSI;

return true;
}

if ($this->hasCoveredMsiOverRequired()) {
$this->actualOverRequiredType = self::COVERED_MSI_OVER_MIN_MSI;

return true;
}

return false;
}

public function getErrorType(): string
{
return $this->failureType;
}

public function getActualOverRequiredType(): string
{
return $this->actualOverRequiredType;
}

public function getMinRequiredValue(): float
{
return $this->failureType === self::MSI_FAILURE ? $this->minMsi : $this->minCoveredMsi;
return
($this->failureType === self::MSI_FAILURE || $this->actualOverRequiredType === self::MSI_OVER_MIN_MSI)
? $this->minMsi : $this->minCoveredMsi;
}

private function hasBadMsi(): bool
Expand All @@ -123,4 +158,22 @@ private function hasBadCoveredMsi(): bool
{
return $this->minCoveredMsi && ($this->metricsCalculator->getCoveredCodeMutationScoreIndicator() < $this->minCoveredMsi);
}

private function hasMsiOverRequired(): bool
{
if ($this->minMsi === 0.0) {
return false;
}

return $this->metricsCalculator->getMutationScoreIndicator() > $this->minMsi + self::VALUE_OVER_REQUIRED_TOLERANCE;
}

private function hasCoveredMsiOverRequired(): bool
{
if ($this->minCoveredMsi === 0.0) {
return false;
}

return $this->metricsCalculator->getCoveredCodeMutationScoreIndicator() > $this->minCoveredMsi + self::VALUE_OVER_REQUIRED_TOLERANCE;
}
}
41 changes: 41 additions & 0 deletions tests/phpunit/Console/ConsoleOutputTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
use Infection\Console\ConsoleOutput;
use Infection\Mutant\Exception\MsiCalculationException;
use Infection\Mutant\MetricsCalculator;
use Infection\Process\Runner\TestRunConstraintChecker;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Style\SymfonyStyle;

Expand Down Expand Up @@ -123,4 +124,44 @@ public function test_log_not_in_control_of_exit_codes(): void
$consoleOutput = new ConsoleOutput($io);
$consoleOutput->logNotInControlOfExitCodes();
}

public function test_log_min_msi_can_get_increased_notice_for_msi(): void
{
$actualMsi = 10.0;
$minMsi = 5.0;
$msiDifference = $actualMsi - $minMsi;

$io = $this->createMock(SymfonyStyle::class);
$io->expects($this->once())->method('note')
->with(
'The MSI is ' . $msiDifference . '% percent points over the required MSI. ' .
'Consider increasing the required MSI percentage the next time you run infection.');
$metricsCalculator = $this->createMock(MetricsCalculator::class);

$metricsCalculator->expects($this->once())->method('getMutationScoreIndicator')
->willReturn($actualMsi);

$consoleOutput = new ConsoleOutput($io);
$consoleOutput->logMinMsiCanGetIncreasedNotice($metricsCalculator, $minMsi, TestRunConstraintChecker::MSI_OVER_MIN_MSI);
}

public function test_log_min_msi_can_get_increased_notice_for_covered_msi(): void
{
$actualMsi = 10.0;
$minMsi = 5.0;
$msiDifference = $actualMsi - $minMsi;

$io = $this->createMock(SymfonyStyle::class);
$io->expects($this->once())->method('note')
->with(
'The Covered Code MSI is ' . $msiDifference . '% percent points over the required Covered Code MSI. ' .
'Consider increasing the required Covered Code MSI percentage the next time you run infection.');
$metricsCalculator = $this->createMock(MetricsCalculator::class);

$metricsCalculator->expects($this->once())->method('getCoveredCodeMutationScoreIndicator')
->willReturn($actualMsi);

$consoleOutput = new ConsoleOutput($io);
$consoleOutput->logMinMsiCanGetIncreasedNotice($metricsCalculator, $minMsi, TestRunConstraintChecker::COVERED_MSI_OVER_MIN_MSI);
}
}
54 changes: 54 additions & 0 deletions tests/phpunit/Exception/InvalidTypeExceptionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?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\Tests\Exception;

use Infection\Exception\InvalidTypeException;
use PHPUnit\Framework\TestCase;

final class InvalidTypeExceptionTest extends TestCase
{
public function test_it_has_correct_user_facing_message(): void
{
$type = 'FooType';

$exception = InvalidTypeException::create($type);

$this->assertSame(
'Invalid type "' . $type . '" passed.',
$exception->getMessage()
);
}
}
60 changes: 60 additions & 0 deletions tests/phpunit/Process/Runner/TestRunConstraintCheckerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@

namespace Infection\Tests\Process\Runner;

use Generator;
use Infection\Mutant\MetricsCalculator;
use Infection\Process\Runner\TestRunConstraintChecker;
use PHPUnit\Framework\TestCase;
Expand Down Expand Up @@ -202,4 +203,63 @@ public function test_run_passes_on_exactly_covered_min_msi(): void

$this->assertTrue($constraintChecker->hasTestRunPassedConstraints());
}

/**
* @dataProvider isMsiOverMinimumMsiProvider
*/
public function test_is_msi_over_minimum_msi_for_msi(?float $actualMsi, float $minMsi, bool $expected): void
{
$metrics = $this->createMock(MetricsCalculator::class);
$metrics->expects($this->once())->method('getMutationScoreIndicator')->willReturn($actualMsi);

$constraintChecker = new TestRunConstraintChecker(
$metrics,
false,
$minMsi,
0.0
);

$this->assertSame($expected, $constraintChecker->isActualOverRequired());
}

/**
* @dataProvider isMsiOverMinimumMsiProvider
*/
public function test_is_msi_over_minimum_msi_for_covered_msi(float $actualCoveredMsi, float $minCoveredMsi, bool $expected): void
{
$metrics = $this->createMock(MetricsCalculator::class);
$metrics->expects($this->once())->method('getCoveredCodeMutationScoreIndicator')->willReturn($actualCoveredMsi);

$constraintChecker = new TestRunConstraintChecker(
$metrics,
false,
0.0,
$minCoveredMsi
);

$this->assertSame($expected, $constraintChecker->isActualOverRequired());
}

public function isMsiOverMinimumMsiProvider(): Generator
{
$minMsi = 10.0;

yield [
'actualMsi' => $minMsi + 1.0,
'minMsi' => $minMsi,
'expected' => true,
];

yield [
'actualMsi' => $minMsi,
'minMsi' => $minMsi,
'expected' => false,
];

yield [
'actualMsi' => $minMsi - 1.0,
'minMsi' => $minMsi,
'expected' => false,
];
}
}