Skip to content

Commit

Permalink
Merge pull request #11 from gacela-project/infection
Browse files Browse the repository at this point in the history
Add infection dependency
  • Loading branch information
Chemaclass committed May 3, 2023
2 parents 901a883 + ccbf0bf commit cf9313a
Show file tree
Hide file tree
Showing 12 changed files with 151 additions and 29 deletions.
31 changes: 31 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,34 @@ jobs:

- name: Run tests
run: vendor/bin/phpunit

mutation-tests:
name: "Mutation Tests"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- uses: shivammathur/setup-php@v2
with:
php-version: 8.0
coverage: xdebug
tools: composer

- name: Get composer cache directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT

- name: Cache dependencies
uses: actions/cache@v3.3.1
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-

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

- name: Run mutation tests
env:
STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }}
run: ./vendor/bin/infection --show-mutations --threads=$(nproc) --min-msi=100 --min-covered-msi=100
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ vendor/

*.cache
composer.lock
mutation-report.html
gacela-custom-services.php
gacela-class-names.php
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
<a href="https://shepherd.dev/github/gacela-project/container">
<img src="https://shepherd.dev/github/gacela-project/container/coverage.svg" alt="Psalm Type-coverage Status">
</a>
<a href="https://dashboard.stryker-mutator.io/reports/github.com/gacela-project/container/main">
<img src="https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fgacela-project%2Fcontainer%2Fmain" alt="Mutation testing badge">
</a>
<a href="https://github.com/gacela-project/container/blob/master/LICENSE">
<img src="https://img.shields.io/badge/License-MIT-green.svg" alt="MIT Software License">
</a>
Expand Down
8 changes: 6 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.16",
"phpunit/phpunit": "^9.6",
"infection/infection": "^0.26",
"phpstan/phpstan": "^1.10",
"psalm/plugin-phpunit": "^0.18",
"symfony/var-dumper": "^5.4",
Expand All @@ -54,15 +55,17 @@
"php": "8.0"
},
"allow-plugins": {
"composer/package-versions-deprecated": true
"composer/package-versions-deprecated": true,
"infection/extension-installer": true
}
},
"scripts": {
"post-install-cmd": ".github/git-hooks/init.sh",
"ctal": [
"@static-clear-cache",
"@csfix",
"@test-all"
"@test-all",
"@infection"
],
"static-clear-cache": ["@clear-cache-psalm", "@clear-cache-phpstan"],
"clear-cache-psalm": "XDEBUG_MODE=off vendor/bin/psalm --clear-cache",
Expand All @@ -83,6 +86,7 @@
"phpstan": "XDEBUG_MODE=off ./vendor/bin/phpstan analyze",
"csfix": "XDEBUG_MODE=off ./vendor/bin/php-cs-fixer fix",
"csrun": "XDEBUG_MODE=off ./vendor/bin/php-cs-fixer fix --dry-run",
"infection": "XDEBUG_MODE=coverage ./vendor/bin/infection --show-mutations --threads=$(nproc 2> /dev/null || sysctl -n hw.logicalcpu) --min-msi=100 --min-covered-msi=100 --test-framework=phpunit",
"phpbench": "XDEBUG_MODE=off ./vendor/bin/phpbench run --report=aggregate --ansi",
"phpbench-base": "XDEBUG_MODE=off ./vendor/bin/phpbench run --tag=baseline --report=aggregate --progress=plain --ansi",
"phpbench-ref": "XDEBUG_MODE=off ./vendor/bin/phpbench run --ref=baseline --report=aggregate --progress=plain --ansi",
Expand Down
18 changes: 18 additions & 0 deletions infection.json5
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
$schema: 'vendor/infection/infection/resources/schema.json',
logs: {
html: 'data/mutation-report.html',
"stryker": {
"badge": "main"
}
},
timeout: 3,
source: {
directories: [
'src'
]
},
mutators: {
'@default': true
}
}
21 changes: 7 additions & 14 deletions src/Container/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ public function has(string $id): bool

public function set(string $id, mixed $instance): void
{
if (isset($this->frozenInstances[$id])) {
throw ContainerException::instanceFrozen($id);
if (!empty($this->frozenInstances[$id])) {
throw ContainerException::frozenInstanceOverride($id);
}

$this->instances[$id] = $instance;
Expand Down Expand Up @@ -111,7 +111,7 @@ public function extend(string $id, Closure $instance): Closure
}

if (isset($this->frozenInstances[$id])) {
throw ContainerException::instanceFrozen($id);
throw ContainerException::frozenInstanceExtend($id);
}

if (is_object($this->instances[$id]) && isset($this->protectedInstances[$this->instances[$id]])) {
Expand Down Expand Up @@ -149,11 +149,10 @@ private function getInstance(string $id): mixed

$rawService = $this->instances[$id];

/** @psalm-suppress InvalidFunctionCall */
$this->instances[$id] = $rawService($this);

/** @var mixed $resolvedService */
$resolvedService = $this->instances[$id];
$resolvedService = $rawService($this);

$this->instances[$id] = $resolvedService;

return $resolvedService;
}
Expand Down Expand Up @@ -204,10 +203,6 @@ private function instantiateClass(string $class): ?object

private function extendLater(string $id, Closure $instance): void
{
if (!isset($this->instancesToExtend[$id])) {
$this->instancesToExtend[$id] = [];
}

$this->instancesToExtend[$id][] = $instance;
}

Expand Down Expand Up @@ -250,12 +245,10 @@ private function extendService(string $id): void
$this->currentlyExtending = $id;

foreach ($this->instancesToExtend[$id] as $instance) {
$extended = $this->extend($id, $instance);
$this->extend($id, $instance);
}

unset($this->instancesToExtend[$id]);
$this->currentlyExtending = null;

$this->set($id, $extended);
}
}
4 changes: 2 additions & 2 deletions src/Container/DependencyResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ private function resolveDependenciesRecursively(ReflectionParameter $parameter):

/** @var class-string $paramTypeName */
$paramTypeName = $paramType->getName();
if ($this->isScalar($paramTypeName) && $parameter->isDefaultValueAvailable()) {
if ($parameter->isDefaultValueAvailable()) {
return $parameter->getDefaultValue();
}

Expand Down Expand Up @@ -103,7 +103,7 @@ private function resolveClass(string $paramTypeName): mixed

$reflection = $this->resolveReflectionClass($paramTypeName);
$constructor = $reflection->getConstructor();
if (!$constructor) {
if ($constructor === null) {
return $reflection->newInstance();
}

Expand Down
11 changes: 8 additions & 3 deletions src/Container/Exception/ContainerException.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,18 @@ public static function instanceNotExtendable(): self
return new self('The passed instance is not extendable.');
}

public static function instanceFrozen(string $id): self
public static function frozenInstanceExtend(string $id): self
{
return new self("The instance '{$id}' is frozen and cannot be extendable.");
return new self("The instance '{$id}' is frozen and cannot be extended.");
}

public static function frozenInstanceOverride(string $id): self
{
return new self("The instance '{$id}' is frozen and cannot be override.");
}

public static function instanceProtected(string $id): self
{
return new self("The instance '{$id}' is protected and cannot be extendable.");
return new self("The instance '{$id}' is protected and cannot be extended.");
}
}
13 changes: 13 additions & 0 deletions tests/Fake/ClassWithDependencyWithoutDependencies.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace GacelaTest\Fake;

final class ClassWithDependencyWithoutDependencies
{
public function __construct(
public ClassWithoutDependencies $classWithoutDependencies,
) {
}
}
13 changes: 13 additions & 0 deletions tests/Fake/ClassWithInnerObjectDependencies.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace GacelaTest\Fake;

final class ClassWithInnerObjectDependencies
{
public function __construct(
public ClassWithRelationship $classWithRelationship,
) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@

namespace GacelaTest\Fake;

final class ClassWithNativeRawDependencies
final class ClassWithRelationship
{
public function __construct(
public string $name,
public int $age,
public Person $person1,
public Person $person2,
) {
}
}
51 changes: 46 additions & 5 deletions tests/Unit/ContainerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@
use ArrayObject;
use Gacela\Container\Container;
use Gacela\Container\Exception\ContainerException;
use GacelaTest\Fake\ClassWithDependencyWithoutDependencies;
use GacelaTest\Fake\ClassWithInnerObjectDependencies;
use GacelaTest\Fake\ClassWithInterfaceDependencies;
use GacelaTest\Fake\ClassWithObjectDependencies;
use GacelaTest\Fake\ClassWithoutDependencies;
use GacelaTest\Fake\ClassWithRelationship;
use GacelaTest\Fake\Person;
use GacelaTest\Fake\PersonInterface;
use PHPUnit\Framework\TestCase;
Expand All @@ -23,13 +26,34 @@ public function test_static_create_without_dependencies(): void
self::assertEquals(new ClassWithoutDependencies(), $actual);
}

public function test_static_create_class_with_inner_dependencies_without_dependencies(): void
{
$actual = Container::create(ClassWithDependencyWithoutDependencies::class);

self::assertEquals(new ClassWithDependencyWithoutDependencies(new ClassWithoutDependencies()), $actual);
}

public function test_static_create_class_with_inner_dependencies_with_many_dependencies(): void
{
$actual = Container::create(ClassWithInnerObjectDependencies::class);

self::assertEquals(new ClassWithInnerObjectDependencies(new ClassWithRelationship(new Person(), new Person())), $actual);
}

public function test_static_create_with_dependencies(): void
{
$actual = Container::create(ClassWithObjectDependencies::class);

self::assertEquals(new ClassWithObjectDependencies(new Person()), $actual);
}

public function test_static_create_with_many_dependencies(): void
{
$actual = Container::create(ClassWithRelationship::class);

self::assertEquals(new ClassWithRelationship(new Person(), new Person()), $actual);
}

public function test_without_dependencies(): void
{
$container = new Container();
Expand Down Expand Up @@ -272,6 +296,23 @@ static function (ArrayObject $arrayObject, Container $container) {
self::assertEquals(new ArrayObject([1, 2, 3, 4]), $actual);
}

public function test_set_after_extend(): void
{
$container = new Container();

$container->extend(
'service_name',
static fn (ArrayObject $arrayObject) => $arrayObject->append(3),
);

$container->set('service_name', static fn () => new ArrayObject([1, 2]));

/** @var ArrayObject $actual */
$actual = $container->get('service_name');

self::assertEquals(new ArrayObject([1, 2, 3]), $actual);
}

public function test_extend_existing_object_service(): void
{
$container = new Container();
Expand Down Expand Up @@ -348,7 +389,7 @@ public function test_extend_existing_used_object_service_is_allowed(): void
$container->set('service_name', new ArrayObject([1, 2]));
$container->get('service_name'); // and get frozen

$this->expectExceptionObject(ContainerException::instanceFrozen('service_name'));
$this->expectExceptionObject(ContainerException::frozenInstanceExtend('service_name'));

$container->extend(
'service_name',
Expand All @@ -362,7 +403,7 @@ public function test_extend_existing_used_callable_service_then_error(): void
$container->set('service_name', static fn () => new ArrayObject([1, 2]));
$container->get('service_name'); // and get frozen

$this->expectExceptionObject(ContainerException::instanceFrozen('service_name'));
$this->expectExceptionObject(ContainerException::frozenInstanceExtend('service_name'));

$container->extend(
'service_name',
Expand All @@ -381,7 +422,7 @@ public function test_extend_later_existing_frozen_object_service_then_error(): v
$container->set('service_name', new ArrayObject([1, 2]));
$container->get('service_name'); // and get frozen

$this->expectExceptionObject(ContainerException::instanceFrozen('service_name'));
$this->expectExceptionObject(ContainerException::frozenInstanceExtend('service_name'));

$container->extend(
'service_name',
Expand All @@ -400,7 +441,7 @@ public function test_extend_later_existing_frozen_callable_service_then_error():
$container->set('service_name', static fn () => new ArrayObject([1, 2]));
$container->get('service_name'); // and get frozen

$this->expectExceptionObject(ContainerException::instanceFrozen('service_name'));
$this->expectExceptionObject(ContainerException::frozenInstanceExtend('service_name'));

$container->extend(
'service_name',
Expand All @@ -414,7 +455,7 @@ public function test_set_existing_frozen_service(): void
$container->set('service_name', static fn () => new ArrayObject([1, 2]));
$container->get('service_name'); // and get frozen

$this->expectExceptionObject(ContainerException::instanceFrozen('service_name'));
$this->expectExceptionObject(ContainerException::frozenInstanceOverride('service_name'));
$container->set('service_name', static fn () => new ArrayObject([3]));
}

Expand Down

0 comments on commit cf9313a

Please sign in to comment.