Skip to content

Commit

Permalink
New logs.badge.matchBranchRegex config to match multiple branches i…
Browse files Browse the repository at this point in the history
…n reporting

By configuring `logs.badge.matchBranchRegex`, it is possible to set multiple branches to report
their mutation score to upstream tools:

```json
{
    "source": {
        "directories": ["src/"]
    },
    "logs": {
        "badge": {
            "matchBranchRegex": "/^release-.*$/"
        }
    }
}
```

Note that `logs.badge.matchBranchRegex` is mutually exclusive with `logs.badge.branch`, which
is much more restrictive: the two cannot be used together.

Fixes infection#1536
  • Loading branch information
Ocramius committed Jul 21, 2021
1 parent 896ca5a commit a0d0b99
Show file tree
Hide file tree
Showing 18 changed files with 278 additions and 95 deletions.
43 changes: 35 additions & 8 deletions resources/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,42 @@
"description": "Markdown file which will give a break-down of the effectiveness of each mutator."
},
"badge": {
"type": "object",
"additionalProperties": false,
"required": ["branch"],
"properties": {
"branch": {
"type": "string",
"description": "Mutation score badge for your GitHub project."
"description": "Mutation score badge for your GitHub project.",
"oneOf": [
{
"type": "object",
"additionalProperties": false,
"required": ["branch"],
"properties": {
"branch": {
"type": "string",
"description": "Mutation score will be uploaded to upstream tracker only if your current branch matches this value.",
"examples": [
"main",
"master",
"develop",
"latest"
]
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": ["matchBranchRegex"],
"properties": {
"matchBranchRegex": {
"type": "string",
"description": "Mutation score will be uploaded to upstream tracker only if your current branch matches this regular expression.",
"examples": [
"/1\\.\\d+/",
"/release-.*/",
"/feature\\/.*/"
]
}
}
}
}
]
},
"github": {
"type": "boolean",
Expand Down
35 changes: 30 additions & 5 deletions src/Configuration/Entry/Badge.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,45 @@

namespace Infection\Configuration\Entry;

use InvalidArgumentException;
use Safe\Exceptions\PcreException;
use function Safe\preg_match;
use function Safe\sprintf;

/**
* @internal
*/
final class Badge
{
private string $branch;
private ?string $exactBranchMatch;
private ?string $matchBranchRegex;

public function __construct(?string $exactBranchMatch, ?string $matchBranchRegex)
{
if ($matchBranchRegex !== null) {
try {
// Yes, the `@` is intentional. For some reason, `thecodingmachine/safe` does not suppress the warnings here
@preg_match($matchBranchRegex, '');
} catch (PcreException $invalidRegex) {
throw new InvalidArgumentException(
sprintf('Provided branchMatchRegex "%s" is not a valid regex', $matchBranchRegex),
0,
$invalidRegex
);
}
}

$this->exactBranchMatch = $exactBranchMatch;
$this->matchBranchRegex = $matchBranchRegex;
}

public function __construct(string $branch)
public function getExactBranchMatch(): ?string
{
$this->branch = $branch;
return $this->exactBranchMatch;
}

public function getBranch(): string
public function getBranchMatchRegEx(): ?string
{
return $this->branch;
return $this->matchBranchRegex;
}
}
7 changes: 4 additions & 3 deletions src/Configuration/Schema/SchemaConfigurationFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,12 @@ private static function createLogs(stdClass $logs): Logs

private static function createBadge(?stdClass $badge): ?Badge
{
$branch = self::normalizeString($badge->branch ?? null);
$exactBranchMatch = self::normalizeString($badge->branch ?? null);
$matchBranchRegex = self::normalizeString($badge->matchBranchRegex ?? null);

return $branch === null
return $exactBranchMatch === null && $matchBranchRegex === null
? null
: new Badge($branch)
: new Badge($exactBranchMatch, $matchBranchRegex)
;
}

Expand Down
30 changes: 23 additions & 7 deletions src/Logger/BadgeLogger.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
use Infection\Logger\Http\StrykerDashboardClient;
use Infection\Metrics\MetricsCalculator;
use Psr\Log\LoggerInterface;
use function Safe\preg_match;
use function Safe\sprintf;

/**
Expand All @@ -54,22 +55,25 @@ final class BadgeLogger implements MutationTestingResultsLogger
private StrykerApiKeyResolver $strykerApiKeyResolver;
private StrykerDashboardClient $strykerDashboardClient;
private MetricsCalculator $metricsCalculator;
private string $branch;
private ?string $exactBranchMatch;
private ?string $matchBranchRegex;
private LoggerInterface $logger;

public function __construct(
BuildContextResolver $buildContextResolver,
StrykerApiKeyResolver $strykerApiKeyResolver,
StrykerDashboardClient $strykerDashboardClient,
MetricsCalculator $metricsCalculator,
string $branch,
?string $exactBranchMatch,
?string $matchBranchRegex,
LoggerInterface $logger
) {
$this->buildContextResolver = $buildContextResolver;
$this->strykerApiKeyResolver = $strykerApiKeyResolver;
$this->strykerDashboardClient = $strykerDashboardClient;
$this->metricsCalculator = $metricsCalculator;
$this->branch = $branch;
$this->exactBranchMatch = $exactBranchMatch;
$this->matchBranchRegex = $matchBranchRegex;
$this->logger = $logger;
}

Expand All @@ -83,11 +87,23 @@ public function log(): void
return;
}

if ($buildContext->branch() !== $this->branch) {
$branch = $buildContext->branch();

if ($this->exactBranchMatch !== null && $branch !== $this->exactBranchMatch) {
$this->logReportWasNotSent(sprintf(
'Expected branch "%s", found "%s"',
$this->branch,
$buildContext->branch()
$this->exactBranchMatch,
$branch
));

return;
}

if ($this->matchBranchRegex !== null && preg_match($this->matchBranchRegex, $branch) !== 1) {
$this->logReportWasNotSent(sprintf(
'Expected branch to match regex "%s", found "%s"',
$this->matchBranchRegex,
$branch
));

return;
Expand All @@ -106,7 +122,7 @@ public function log(): void

$this->strykerDashboardClient->sendReport(
'github.com/' . $buildContext->repositorySlug(),
$buildContext->branch(),
$branch,
$apiKey,
$this->metricsCalculator->getMutationScoreIndicator()
);
Expand Down
7 changes: 5 additions & 2 deletions src/Logger/BadgeLoggerFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ public function __construct(

public function createFromLogEntries(Logs $logConfig): ?MutationTestingResultsLogger
{
if ($logConfig->getBadge() === null) {
$badge = $logConfig->getBadge();

if ($badge === null) {
return null;
}

Expand All @@ -78,7 +80,8 @@ public function createFromLogEntries(Logs $logConfig): ?MutationTestingResultsLo
$this->logger
),
$this->metricsCalculator,
$logConfig->getBadge()->getBranch(),
$badge->getExactBranchMatch(),
$badge->getBranchMatchRegEx(),
$this->logger
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ static function (SplFileInfo $fileInfo): Trace {
);

$mutators = $container->getMutatorFactory()->create(
$container->getMutatorResolver()->resolve(['@default' => true])
$container->getMutatorResolver()->resolve(['@default' => true]),
true
);

$fileMutationGenerator = $container->getFileMutationGenerator();
Expand Down
3 changes: 2 additions & 1 deletion tests/benchmark/Tracing/provide-traces-closure.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@
Container::DEFAULT_DRY_RUN,
Container::DEFAULT_GIT_DIFF_FILTER,
Container::DEFAULT_GIT_DIFF_BASE,
Container::DEFAULT_USE_GITHUB_LOGGER
Container::DEFAULT_USE_GITHUB_LOGGER,
true
);

$generateTraces = static function (?int $maxCount) use ($container): iterable {
Expand Down
4 changes: 2 additions & 2 deletions tests/phpunit/Configuration/ConfigurationFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,7 @@ public function valueProvider(): iterable
'debug.log',
'mutator.log',
true,
new Badge('master')
new Badge('master', null)
),
'config/tmp',
new PhpUnit(
Expand Down Expand Up @@ -785,7 +785,7 @@ public function valueProvider(): iterable
'debug.log',
'mutator.log',
true,
new Badge('master')
new Badge('master', null)
),
'none',
'/path/to/config/tmp/infection',
Expand Down
2 changes: 1 addition & 1 deletion tests/phpunit/Configuration/ConfigurationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ public function valueProvider(): iterable
'debug.log',
'mutator.log',
true,
new Badge('master')
new Badge('master', null)
),
'default',
'custom-dir',
Expand Down
48 changes: 0 additions & 48 deletions tests/phpunit/Configuration/Entry/BadgeAssertions.php

This file was deleted.

29 changes: 25 additions & 4 deletions tests/phpunit/Configuration/Entry/BadgeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,37 @@
namespace Infection\Tests\Configuration\Entry;

use Infection\Configuration\Entry\Badge;
use InvalidArgumentException;
use PHPUnit\Framework\TestCase;

final class BadgeTest extends TestCase
{
use BadgeAssertions;
public function test_it_can_be_instantiated_with_an_exact_branch_match(): void
{
$badge = new Badge('master', null);

$this->assertNull($badge->getBranchMatchRegEx());
$this->assertSame('master', $badge->getExactBranchMatch());
}

public function test_it_can_be_instantiated_with_a_regex_branch_match(): void
{
$badge = new Badge(null, '/^foo$/');

$this->assertNull($badge->getExactBranchMatch());
$this->assertSame('/^foo$/', $badge->getBranchMatchRegEx());
}

public function test_it_can_be_instantiated(): void
public function test_it_rejects_invalid_regex(): void
{
$badge = new Badge('master');
try {
new Badge(null, '/foo#');

$this->assertBadgeStateIs($badge, 'master');
$this->fail();
} catch (InvalidArgumentException $invalid) {
$this->assertSame('Provided branchMatchRegex "/foo#" is not a valid regex', $invalid->getMessage());
$this->assertSame(0, $invalid->getCode());
$this->assertNotNull($invalid->getPrevious());
}
}
}
5 changes: 2 additions & 3 deletions tests/phpunit/Configuration/Entry/LogsAssertions.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@

trait LogsAssertions
{
use BadgeAssertions;

private function assertLogsStateIs(
Logs $logs,
?string $expectedTextLogFilePath,
Expand All @@ -65,7 +63,8 @@ private function assertLogsStateIs(
$this->assertNull($badge);
} else {
$this->assertNotNull($badge);
$this->assertBadgeStateIs($badge, $expectedBadge->getBranch());

self::assertEquals($expectedBadge, $badge);
}
}
}
2 changes: 1 addition & 1 deletion tests/phpunit/Configuration/Entry/LogsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public function valuesProvider(): iterable
'debug.log',
'perMutator.log',
true,
new Badge('master'),
new Badge('master', null),
];
}
}

0 comments on commit a0d0b99

Please sign in to comment.