Skip to content

Commit

Permalink
Add mutation testing
Browse files Browse the repository at this point in the history
  • Loading branch information
paulbalandan committed Sep 10, 2024
1 parent c28aa74 commit 3b0b2e1
Show file tree
Hide file tree
Showing 11 changed files with 432 additions and 9 deletions.
13 changes: 12 additions & 1 deletion .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ jobs:
${{ github.workflow }}-PHP_${{ matrix.php-version }}-
- name: Install dependencies
run: composer update --ansi --no-scripts ${{ steps.globals.outputs.EXPERIMENTAL_FLAG }}
run: composer update --ansi ${{ steps.globals.outputs.EXPERIMENTAL_FLAG }}

- name: Run Unit Tests
shell: bash
Expand All @@ -73,6 +73,17 @@ jobs:
COVERAGE_OPTION: ${{ matrix.os != 'windows-2019' && '--coverage' || '' }}
TACHYCARDIA_MONITOR_GA: enabled

- name: Run Mutation Testing
shell: bash
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
composer mutation:filter
else
composer mutation:check
fi
env:
INFECTION_DASHBOARD_API_KEY: ${{ secrets.INFECTION_DASHBOARD_API_KEY }}

- name: Display structure of coverage files
if: matrix.os != 'windows-2019'
run: ls -la
Expand Down
1 change: 1 addition & 0 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
])
->append([
__FILE__,
'bin/build-infection',
'bin/parallel-phpunit',
'bin/prune-cache',
])
Expand Down
15 changes: 15 additions & 0 deletions bin/build-infection
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env php
<?php

declare(strict_types=1);

/**
* This file is part of the Nexus framework.
*
* (c) John Paul E. Balandan, CPA <[email protected]>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

require __DIR__.'/../tools/build-infection.php';
4 changes: 4 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@
],
"cs:check": "tools/vendor/bin/php-cs-fixer check --ansi --verbose --diff",
"cs:fix": "tools/vendor/bin/php-cs-fixer fix --ansi --verbose --diff",
"mutation:check": "tools/vendor/bin/infection --threads=max --ansi",
"mutation:filter": "@mutation:check --git-diff-filter=AM --git-diff-base=origin/1.x",
"phpstan:baseline": "phpstan analyse --ansi --generate-baseline=phpstan-baseline.php",
"phpstan:check": "phpstan analyse --ansi --verbose",
"test:all": [
Expand All @@ -93,6 +95,8 @@
"scripts-descriptions": {
"cs:check": "Checks for coding style violations",
"cs:fix": "Fixes any coding style violations",
"mutation:check": "Runs Infection on whole codebase",
"mutation:filter": "Runs Infection on added and modified files only",
"phpstan:baseline": "Runs PHPStan and dumps resulting errors to baseline",
"phpstan:check": "Runs PHPStan with identifiers support",
"test:all": "Runs all PHPUnit tests",
Expand Down
182 changes: 182 additions & 0 deletions infection.json5
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
{
"$schema": "./tools/vendor/infection/infection/resources/schema.json",
"source": {
"directories": [
"src/Nexus"
],
"excludes": [
"PHPStan"
]
},
"timeout": 10,
"logs": {
"text": "build/logs/infection/infection.log",
"html": "build/logs/infection/infection.html",
"stryker": {
"badge": "1.x"
}
},
"tmpDir": "build",
"minMsi": 90,
"minCoveredMsi": 90,
"mutators": {
"ArrayItem": true,
"ArrayItemRemoval": true,
"ArrayOneItem": true,
"AssignCoalesce": true,
"Assignment": true,
"AssignmentEqual": true,
"BCMath": true,
"BitwiseAnd": true,
"BitwiseNot": true,
"BitwiseOr": true,
"BitwiseXor": true,
"Break_": true,
"CastArray": true,
"CastBool": true,
"CastFloat": true,
"CastInt": {
"ignore": [
"Nexus\\Clock\\SystemClock"
]
},
"CastObject": true,
"CastString": true,
"CatchBlockRemoval": true,
"Catch_": true,
"CloneRemoval": true,
"Coalesce": true,
"ConcatOperandRemoval": true,
"Continue_": true,
"Decrement": true,
"DivEqual": true,
"Division": {
"ignore": [
"Nexus\\Clock\\SystemClock"
]
},
"DoWhile": true,
"ElseIfNegation": true,
"Equal": true,
"EqualIdentical": true,
"Exponentiation": true,
"FalseValue": true,
"Finally_": true,
"FloatNegation": true,
"For_": true,
"Foreach_": true,
"FunctionCall": true,
"GreaterThanNegotiation": true,
"GreaterThanOrEqualToNegotiation": true,
"Identical": true,
"IfNegation": true,
"Increment": true,
"InstanceOf_": true,
"LessThanNegotiation": true,
"LessThanOrEqualToNegotiation": true,
"LogicalAnd": true,
"LogicalAndAllSubExprNegation": true,
"LogicalAndNegation": true,
"LogicalAndSingleSubExprNegation": true,
"LogicalLowerAnd": true,
"LogicalLowerOr": true,
"LogicalNot": true,
"LogicalOr": true,
"LogicalOrAllSubExprNegation": true,
"LogicalOrNegation": true,
"LogicalOrSingleSubExprNegation": true,
"MBString": true,
"MatchArmRemoval": true,
"MethodCallRemoval": true,
"MinusEqual": true,
"ModEqual": {
"ignore": [
"Nexus\\Clock\\SystemClock"
]
},
"Modulus": true,
"MulEqual": true,
"Multiplication": true,
"NewObject": true,
"NotEqual": true,
"NotEqualNotIdentical": true,
"NullSafeMethodCall": true,
"NullSafePropertyCall": true,
"OneZeroFloat": true,
"PlusEqual": true,
"PowEqual": true,
"PregMatchMatches": true,
"PregMatchRemoveCaret": true,
"PregMatchRemoveDollar": true,
"PregMatchRemoveFlags": true,
"PregQuote": true,
"ProtectedVisibility": true,
"PublicVisibility": true,
"SharedCaseRemoval": true,
"ShiftLeft": true,
"ShiftRight": true,
"Spaceship": true,
"SpreadAssignment": true,
"SpreadOneItem": true,
"SpreadRemoval": true,
"Ternary": true,
"This": true,
"Throw_": true,
"TrueValue": true,
"UnwrapArrayChangeKeyCase": true,
"UnwrapArrayChunk": true,
"UnwrapArrayColumn": true,
"UnwrapArrayCombine": true,
"UnwrapArrayDiff": true,
"UnwrapArrayDiffAssoc": true,
"UnwrapArrayDiffKey": true,
"UnwrapArrayDiffUassoc": true,
"UnwrapArrayDiffUkey": true,
"UnwrapArrayFilter": true,
"UnwrapArrayFlip": true,
"UnwrapArrayIntersect": true,
"UnwrapArrayIntersectAssoc": true,
"UnwrapArrayIntersectKey": true,
"UnwrapArrayIntersectUassoc": true,
"UnwrapArrayIntersectUkey": true,
"UnwrapArrayKeys": true,
"UnwrapArrayMap": true,
"UnwrapArrayMerge": true,
"UnwrapArrayMergeRecursive": true,
"UnwrapArrayPad": true,
"UnwrapArrayReduce": true,
"UnwrapArrayReplace": true,
"UnwrapArrayReplaceRecursive": true,
"UnwrapArrayReverse": true,
"UnwrapArraySlice": true,
"UnwrapArraySplice": true,
"UnwrapArrayUdiff": true,
"UnwrapArrayUdiffAssoc": true,
"UnwrapArrayUdiffUassoc": true,
"UnwrapArrayUintersect": true,
"UnwrapArrayUintersectAssoc": true,
"UnwrapArrayUintersectUassoc": true,
"UnwrapArrayUnique": true,
"UnwrapArrayValues": true,
"UnwrapFinally": true,
"UnwrapLcFirst": true,
"UnwrapLtrim": true,
"UnwrapRtrim": true,
"UnwrapStrIreplace": true,
"UnwrapStrRepeat": true,
"UnwrapStrReplace": true,
"UnwrapStrRev": true,
"UnwrapStrShuffle": true,
"UnwrapStrToLower": true,
"UnwrapStrToUpper": true,
"UnwrapSubstr": true,
"UnwrapTrim": true,
"UnwrapUcFirst": true,
"UnwrapUcWords": true,
"While_": true,
"YieldValue": true,
"Yield_": true
},
"testFramework": "phpunit",
"testFrameworkOptions": "--group=unit-test"
}
1 change: 1 addition & 0 deletions phpstan.dist.neon
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ parameters:
analyseAndScan:
- tests/**/data/**
- tests/PHPStan/**/data/**
analyse:
- tools/vendor/**
bootstrapFiles:
- vendor/autoload.php
Expand Down
47 changes: 47 additions & 0 deletions tests/AutoReview/InfectionConfigTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

/**
* This file is part of the Nexus framework.
*
* (c) John Paul E. Balandan, CPA <[email protected]>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

namespace Nexus\Tests\AutoReview;

use Nexus\Tools\InfectionConfigBuilder;
use PHPUnit\Framework\Attributes\CoversNothing;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\TestCase;

/**
* @internal
*/
#[CoversNothing]
#[Group('auto-review')]
final class InfectionConfigTest extends TestCase
{
public function testInfectionJsonIsUpdated(): void
{
if (is_file(__DIR__.'/../../tools/vendor/autoload.php')) {
require_once __DIR__.'/../../tools/vendor/autoload.php';
} else {
self::markTestSkipped('Install `tools` to run this test.');
}

$infectionJson = file_get_contents(__DIR__.'/../../infection.json5');
self::assertIsString($infectionJson);

$actualConfig = json_decode($infectionJson, true);
self::assertIsArray($actualConfig);
self::assertSame(
InfectionConfigBuilder::build(),
$actualConfig,
'The infection.json5 is not updated; run `bin/build-infection` to update.',
);
}
}
36 changes: 28 additions & 8 deletions tests/Option/OptionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,11 @@ public function testOptionMap(): void
$option = (new Some('Hello, World!'))->map($predicate);
self::assertTrue($option->isSome());
self::assertSame(13, $option->unwrap());
self::assertTrue((new None())->map($predicate)->isNone());

$none = new None();
$newNone = $none->map($predicate);
self::assertTrue($newNone->isNone());
self::assertNotSame($newNone, $none);
}

public function testOptionMapOr(): void
Expand All @@ -105,9 +109,11 @@ public function testOptionMapOrElse(): void

public function testOptionAnd(): void
{
self::assertTrue((new Some(2))->and(new None())->isNone());
self::assertTrue((new None())->and(new Some('foo'))->isNone());
self::assertTrue((new None())->and(new None())->isNone());
$none = new None();
self::assertTrue((new Some(2))->and($none)->isNone());
self::assertTrue($none->and(new Some('foo'))->isNone());
self::assertTrue($none->and($none)->isNone());
self::assertNotSame($none, $none->and($none));

$option = (new Some(2))->and(new Some('foo'));
self::assertInstanceOf(Some::class, $option);
Expand All @@ -121,16 +127,25 @@ public function testOptionAndThen(): void
$option = (new Some(2))->andThen($squareThenToString);
self::assertTrue($option->isSome());
self::assertSame('4', $option->unwrap());
self::assertTrue((new None())->andThen($squareThenToString)->isNone());

$none = new None();
self::assertTrue($none->andThen($squareThenToString)->isNone());
self::assertNotSame($none, $none->andThen($squareThenToString));
}

public function testOptionFilter(): void
{
$isEven = static fn(int $n): bool => $n % 2 === 0;

self::assertTrue((new None())->filter($isEven)->isNone());
$none = new None();
self::assertTrue($none->filter($isEven)->isNone());
self::assertNotSame($none, $none->filter($isEven));

self::assertFalse((new Some(3))->filter($isEven)->isSome());
self::assertTrue((new Some(4))->filter($isEven)->isSome());

$some = new Some(4);
self::assertTrue($some->filter($isEven)->isSome());
self::assertNotSame($some, $some->filter($isEven));
}

public function testOptionOr(): void
Expand All @@ -141,6 +156,7 @@ public function testOptionOr(): void

self::assertTrue($some02->or($none)->isSome());
self::assertSame(2, $some02->or($none)->unwrap());
self::assertNotSame($some02, $some02->or($none));

self::assertTrue($none->or($some100)->isSome());
self::assertSame(100, $none->or($some100)->unwrap());
Expand All @@ -156,8 +172,10 @@ public function testOptionOrElse(): void
$nobody = static fn(): None => new None();
$vikings = static fn(): Some => new Some('vikings');

$option = (new Some('barbarians'))->orElse($vikings);
$some = new Some('barbarians');
$option = $some->orElse($vikings);
self::assertTrue($option->isSome());
self::assertNotSame($some, $option);
self::assertSame('barbarians', $option->unwrap());

$option = (new None())->orElse($vikings);
Expand All @@ -174,12 +192,14 @@ public function testOptionXor(): void

self::assertInstanceOf(Some::class, $some->xor($none));
self::assertSame(2, $some->xor($none)->unwrap());
self::assertNotSame($some, $some->xor($none));

self::assertInstanceOf(Some::class, $none->xor($some));
self::assertSame(2, $none->xor($some)->unwrap());

self::assertInstanceOf(None::class, $some->xor($some));
self::assertInstanceOf(None::class, $none->xor($none));
self::assertNotSame($none, $none->xor($none));
}

public function testOptionIteration(): void
Expand Down
Loading

0 comments on commit 3b0b2e1

Please sign in to comment.