Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add infection dependency #11

Merged
merged 14 commits into from
May 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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
Loading