Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 9b118ac

Browse files
authored
Set up mutation testing
1 parent 38fc20d commit 9b118ac

File tree

4 files changed

+144
-0
lines changed

4 files changed

+144
-0
lines changed

‎.github/workflows/build.yml‎

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,55 @@ jobs:
145145
- name: "Tests"
146146
run: "make tests"
147147

148+
mutation-testing:
149+
name: "Mutation Testing"
150+
runs-on: "ubuntu-latest"
151+
needs: ["tests", "static-analysis"]
152+
153+
strategy:
154+
fail-fast: false
155+
matrix:
156+
php-version:
157+
- "8.2"
158+
- "8.3"
159+
- "8.4"
160+
161+
steps:
162+
- name: "Checkout"
163+
uses: actions/checkout@v5
164+
165+
- name: "Install PHP"
166+
uses: "shivammathur/setup-php@v2"
167+
with:
168+
coverage: "pcov"
169+
php-version: "${{ matrix.php-version }}"
170+
ini-file: development
171+
extensions: pdo, mysqli, pgsql, pdo_mysql, pdo_pgsql, pdo_sqlite, mongodb
172+
tools: infection:0.31.4
173+
174+
- name: "Allow installing on PHP 8.4"
175+
if: matrix.php-version == '8.4'
176+
run: "composer config platform.php 8.3.99"
177+
178+
- name: "Install dependencies"
179+
run: "composer install --no-interaction --no-progress"
180+
181+
- uses: "actions/download-artifact@v4"
182+
with:
183+
name: "result-cache-${{ matrix.php-version }}"
184+
path: "tmp/"
185+
186+
- name: "Run infection"
187+
run: |
188+
git fetch --depth=1 origin $GITHUB_BASE_REF
189+
infection --git-diff-base=origin/$GITHUB_BASE_REF --git-diff-lines --ignore-msi-with-no-mutations --min-msi=100 --min-covered-msi=100 --log-verbosity=all --debug
190+
191+
- uses: "actions/upload-artifact@v4"
192+
if: always()
193+
with:
194+
name: "infection-log-${{ matrix.php-version }}"
195+
path: "tmp/infection.log"
196+
148197
static-analysis:
149198
name: "PHPStan"
150199
runs-on: "ubuntu-latest"
@@ -189,3 +238,9 @@ jobs:
189238

190239
- name: "PHPStan"
191240
run: "make phpstan"
241+
242+
- uses: "actions/upload-artifact@v4"
243+
with:
244+
# "update-packages" is not relevant for the download-artifact counterpart, but we need it here to get unique artifact names across all jobs
245+
name: "result-cache-${{ matrix.php-version }}${{ matrix.update-packages && '-packages-updated' || '' }}"
246+
path: "tmp/resultCache.php"

‎infection.json5‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"$schema": "vendor/infection/infection/resources/schema.json",
3+
"timeout": 30,
4+
"source": {
5+
"directories": [
6+
"src"
7+
]
8+
},
9+
"staticAnalysisTool": "phpstan",
10+
"logs": {
11+
"text": "tmp/infection.log"
12+
},
13+
"mutators": {
14+
"@default": false,
15+
"PHPStan\\Infection\\TrinaryLogicMutator": true
16+
}
17+
}

‎phpstan.neon‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@ parameters:
1818
- src
1919
- tests
2020

21+
resultCachePath: tmp/resultCache.php
22+
2123
excludePaths:
2224
- tests/*/data/*
2325
- tests/*/data-attributes/*
2426
- tests/*/data-php-*/*
2527
- tests/Rules/Doctrine/ORM/entity-manager.php
28+
- tests/Infection/
2629

2730
reportUnmatchedIgnoredErrors: false
2831

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Infection;
4+
5+
use Infection\Mutator\Definition;
6+
use Infection\Mutator\Mutator;
7+
use Infection\Mutator\MutatorCategory;
8+
use LogicException;
9+
use PhpParser\Node;
10+
use function in_array;
11+
12+
/**
13+
* @implements Mutator<Node\Expr\MethodCall>
14+
*/
15+
final class TrinaryLogicMutator implements Mutator
16+
{
17+
18+
public static function getDefinition(): Definition
19+
{
20+
return new Definition(
21+
<<<'TXT'
22+
Replaces TrinaryLogic->yes() with !TrinaryLogic->no() and vice versa.
23+
TXT
24+
,
25+
MutatorCategory::ORTHOGONAL_REPLACEMENT,
26+
null,
27+
<<<'DIFF'
28+
- $type->isBoolean()->yes();
29+
+ !$type->isBoolean()->no();
30+
DIFF,
31+
);
32+
}
33+
34+
public function getName(): string
35+
{
36+
return 'TrinaryLogicMutator';
37+
}
38+
39+
public function canMutate(Node $node): bool
40+
{
41+
if (!$node instanceof Node\Expr\MethodCall) {
42+
return false;
43+
}
44+
45+
if (!$node->name instanceof Node\Identifier) {
46+
return false;
47+
}
48+
49+
if (!in_array($node->name->name, ['yes', 'no'], true)) {
50+
return false;
51+
}
52+
53+
return true;
54+
}
55+
56+
public function mutate(Node $node): iterable
57+
{
58+
if (!$node->name instanceof Node\Identifier) {
59+
throw new LogicException();
60+
}
61+
62+
if ($node->name->name === 'yes') {
63+
yield new Node\Expr\BooleanNot(new Node\Expr\MethodCall($node->var, 'no'));
64+
} else {
65+
yield new Node\Expr\BooleanNot(new Node\Expr\MethodCall($node->var, 'yes'));
66+
}
67+
}
68+
69+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /