Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
8a4e7cd
Setup mutation testing
staabm Oct 4, 2025
65db2c8
Update platform-test.yml
staabm Oct 4, 2025
d3a6e60
fix built
staabm Oct 4, 2025
69b69ca
fix build
staabm Oct 4, 2025
baab8a3
run infection in all test jobs
staabm Oct 5, 2025
b44bb9c
Update platform-test.yml
staabm Oct 5, 2025
d223186
Update platform-test.yml
staabm Oct 5, 2025
843fbfe
Update platform-test.yml
staabm Oct 5, 2025
7a2ece5
Update platform-test.yml
staabm Oct 5, 2025
788a450
cs
staabm Oct 5, 2025
7517bac
Update platform-test.yml
staabm Oct 5, 2025
0cf3164
Update platform-test.yml
staabm Oct 5, 2025
811f79d
Update build.yml
staabm Oct 6, 2025
cf457bb
Discard changes to .github/workflows/platform-test.yml
staabm Oct 6, 2025
547569a
Update build.yml
staabm Oct 6, 2025
ba0c6b8
Update TrinaryLogicMutator.php
staabm Oct 6, 2025
bcc667f
Update build.yml
staabm Oct 6, 2025
6075749
feedback
staabm Oct 6, 2025
71c74c4
Update build.yml
staabm Oct 6, 2025
c180a99
Update build.yml
staabm Oct 6, 2025
ae2e5ff
Cannot pass both "--git-diff-lines" and "--git-diff-filter" options
staabm Oct 6, 2025
e680440
Update DoctrineTypeUtils.php
staabm Oct 6, 2025
1398077
Update composer.json
staabm Oct 6, 2025
d1637c4
Update QueryBuilderDqlRule.php
staabm Oct 6, 2025
80978eb
Update DoctrineTypeUtils.php
staabm Oct 6, 2025
054bb5e
Update build.yml
staabm Oct 6, 2025
2ae9a03
Update QueryBuilderDqlRule.php
staabm Oct 6, 2025
fb3db22
Update infection.json5
staabm Oct 6, 2025
c37fc3f
Update build.yml
staabm Oct 6, 2025
871cd46
Update build.yml
staabm Oct 7, 2025
7e0fd4a
Update infection.json5
staabm Oct 7, 2025
946fc28
Update phpstan.neon
staabm Oct 7, 2025
e509b9a
Update build.yml
staabm Oct 7, 2025
2eecbb9
use local temp
staabm Oct 7, 2025
704bce2
unique artifact name
staabm Oct 7, 2025
19706cb
Update build.yml
staabm Oct 7, 2025
2caea8d
Update build.yml
staabm Oct 7, 2025
9f9de8f
fix
staabm Oct 7, 2025
c4d823e
Update infection.json5
staabm Oct 7, 2025
93d3d97
Update infection.json5
staabm Oct 7, 2025
01a9197
Update infection.json5
staabm Oct 7, 2025
58e765a
simplify
staabm Oct 7, 2025
378e518
increase timeout
staabm Oct 7, 2025
b3b8b99
Update infection.json5
staabm Oct 7, 2025
67cf382
upload infection log as artifact to ease debugging
staabm Oct 7, 2025
c0e5252
Update build.yml
staabm Oct 7, 2025
d954a45
enable debugging
staabm Oct 7, 2025
13ad641
pin infection version
staabm Oct 7, 2025
3bddff7
update to infection:0.31.3
staabm Oct 7, 2025
91d8b14
pin infection:0.31.4
staabm Oct 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,54 @@ jobs:
- name: "Tests"
run: "make tests"

mutation-testing:
name: "Mutation Testing"
runs-on: "ubuntu-latest"
needs: ["tests", "static-analysis"]
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Phpunit killer requires a green test run
Phpstan killer requires a green phpstan run


strategy:
fail-fast: false
matrix:
php-version:
- "8.2"
- "8.3"
- "8.4"

steps:
- name: "Checkout"
uses: actions/checkout@v5

- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
coverage: "pcov"
php-version: "${{ matrix.php-version }}"
ini-file: development
extensions: pdo, mysqli, pgsql, pdo_mysql, pdo_pgsql, pdo_sqlite, mongodb
tools: infection:0.31.4

- name: "Allow installing on PHP 8.4"
if: matrix.php-version == '8.4'
run: "composer config platform.php 8.3.99"

- name: "Install dependencies"
run: "composer install --no-interaction --no-progress"

- uses: "actions/download-artifact@v4"
with:
name: "result-cache-${{ matrix.php-version }}"
path: "tmp/"

- name: "Run infection"
run: |
git fetch --depth=1 origin $GITHUB_BASE_REF
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

- uses: "actions/upload-artifact@v4"
with:
name: "infection-log-${{ matrix.php-version }}"
path: "tmp/infection.log"

static-analysis:
name: "PHPStan"
runs-on: "ubuntu-latest"
Expand Down Expand Up @@ -189,3 +237,9 @@ jobs:

- name: "PHPStan"
run: "make phpstan"

- uses: "actions/upload-artifact@v4"
with:
# "update-packages" is not relevant for the download-artifact counterpart, but we need it here to get unique artifact names across all jobs
name: "result-cache-${{ matrix.php-version }}${{ matrix.update-packages && '-packages-updated' || '' }}"
path: "tmp/resultCache.php"
17 changes: 17 additions & 0 deletions infection.json5
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"$schema": "vendor/infection/infection/resources/schema.json",
"timeout": 30,
"source": {
"directories": [
"src"
]
},
"staticAnalysisTool": "phpstan",
"logs": {
"text": "tmp/infection.log"
},
"mutators": {
"@default": false,
"PHPStan\\Infection\\TrinaryLogicMutator": true
}
}
3 changes: 3 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,14 @@ parameters:
- src
- tests

resultCachePath: tmp/resultCache.php

excludePaths:
- tests/*/data/*
- tests/*/data-attributes/*
- tests/*/data-php-*/*
- tests/Rules/Doctrine/ORM/entity-manager.php
- tests/Infection/

reportUnmatchedIgnoredErrors: false

Expand Down
9 changes: 9 additions & 0 deletions src/Rules/Doctrine/ORM/QueryBuilderDqlRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,15 @@
return [];
}

// testing stuff
Copy link
Contributor Author

Choose a reason for hiding this comment

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

TODO: revert testing change

$obj = (new ObjectType('Doctrine\ORM\QueryBuilder'))->isSuperTypeOf($calledOnType);
if ($obj->yes()) {

Check warning on line 74 in src/Rules/Doctrine/ORM/QueryBuilderDqlRule.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.4)

Escaped Mutant for Mutator "TrinaryLogicMutator": @@ @@ } // testing stuff $obj = (new ObjectType('Doctrine\ORM\QueryBuilder'))->isSuperTypeOf($calledOnType); - if ($obj->yes()) { + if (!$obj->no()) { $x = 1; } else { $x = 2;

Check warning on line 74 in src/Rules/Doctrine/ORM/QueryBuilderDqlRule.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.3)

Escaped Mutant for Mutator "TrinaryLogicMutator": @@ @@ } // testing stuff $obj = (new ObjectType('Doctrine\ORM\QueryBuilder'))->isSuperTypeOf($calledOnType); - if ($obj->yes()) { + if (!$obj->no()) { $x = 1; } else { $x = 2;

Check warning on line 74 in src/Rules/Doctrine/ORM/QueryBuilderDqlRule.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.2)

Escaped Mutant for Mutator "TrinaryLogicMutator": @@ @@ } // testing stuff $obj = (new ObjectType('Doctrine\ORM\QueryBuilder'))->isSuperTypeOf($calledOnType); - if ($obj->yes()) { + if (!$obj->no()) { $x = 1; } else { $x = 2;
$x = 1;
} else {
$x = 2;
}


try {
$dqlType = $scope->getType(new MethodCall($node, new Node\Identifier('getDQL'), []));
} catch (Throwable $e) {
Expand Down
69 changes: 69 additions & 0 deletions tests/Infection/TrinaryLogicMutator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php declare(strict_types = 1);

namespace PHPStan\Infection;

use Infection\Mutator\Definition;
use Infection\Mutator\Mutator;
use Infection\Mutator\MutatorCategory;
use LogicException;
use PhpParser\Node;
use function in_array;

/**
* @implements Mutator<Node\Expr\MethodCall>
*/
final class TrinaryLogicMutator implements Mutator
{

public static function getDefinition(): Definition
{
return new Definition(
<<<'TXT'
Replaces TrinaryLogic->yes() with !TrinaryLogic->no() and vice versa.
TXT
,
MutatorCategory::ORTHOGONAL_REPLACEMENT,
null,
<<<'DIFF'
- $type->isBoolean()->yes();
+ !$type->isBoolean()->no();
DIFF,
);
}

public function getName(): string
{
return 'TrinaryLogicMutator';
}

public function canMutate(Node $node): bool
Copy link
Contributor Author

Choose a reason for hiding this comment

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

atm this class replaces any call to a method named yes, or no.
for now it is not scoped to the TrinaryLogic class.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

atm this mutator works for these classes in the phpstan-src codebase - which I think is not a bad thing

grafik

{
if (!$node instanceof Node\Expr\MethodCall) {
return false;
}

if (!$node->name instanceof Node\Identifier) {
return false;
}

if (!in_array($node->name->name, ['yes', 'no'], true)) {
return false;
}

return true;
}

public function mutate(Node $node): iterable
{
if (!$node->name instanceof Node\Identifier) {
throw new LogicException();
}

if ($node->name->name === 'yes') {
yield new Node\Expr\BooleanNot(new Node\Expr\MethodCall($node->var, 'no'));
} else {
yield new Node\Expr\BooleanNot(new Node\Expr\MethodCall($node->var, 'yes'));
}
}

}
Loading