-## Installation
+## Features
+
+✅ **Advanced Reflection Utilities**
+- Access and modify private and protected properties via reflection.
+- Invoke inaccessible methods to expand testing coverage.
+
+✅ **Cross-Platform String Assertions**
+- Eliminate false positives/negatives caused by Windows vs. Unix line-ending differences.
+- Normalize line endings for consistent string comparisons across platforms.
+
+✅ **File System Test Management**
+- Recursively clean files and directories for isolated test environments.
+- Safe removal that preserves Git-tracking files (for example, '.gitignore', '.gitkeep').
+
+## Quick start
+
+### System requirements
+
+- [`PHP`](https://www.php.net/downloads) 8.1 or higher.
+- [`Composer`](https://getcomposer.org/download/) for dependency management.
+- [`PHPUnit`](https://phpunit.de/) for testing framework integration.
+
+### Installation
-The preferred way to install this extension is through [composer](https://getcomposer.org/download/).
+#### Method 1: Using [Composer](https://getcomposer.org/download/) (recommended)
-Either run
+Install the extension.
-```shell
-composer require --prefer-dist php-forge/support
+```bash
+composer require --dev --prefer-dist php-forge/support:^0.1
```
-or add
+#### Method 2: Manual installation
+
+Add to your `composer.json`.
```json
-"php-forge/support": "^0.1"
+{
+ "require-dev": {
+ "php-forge/support": "^0.1"
+ }
+}
```
-to the require-dev section of your `composer.json` file.
+Then run.
+
+```bash
+composer update
+```
-## Usage
+## Basic Usage
-### Equals without line ending
+### Accessing private properties
```php
assertSame('bar', Assert::inaccessibleProperty($object, 'foo'));
+// normalize line endings for consistent comparisons
+Assert::equalsWithoutLE(
+ "Foo\r\nBar",
+ "Foo\nBar",
+ "Should match regardless of line ending style"
+);
```
-### Invoke method
+### Invoking protected methods
```php
assertSame('foo', Assert::invokeMethod($object, 'foo'));
+// test protected method behavior
+$result = Assert::invokeMethod($object, 'calculate', [5, 3]);
+
+self::assertSame(8, $result);
```
-### Set inaccessible property
+### Remove files from directory
```php
assertSame('baz', Assert::inaccessibleProperty($object, 'foo'));
+// clean up test artifacts (preserves '.gitignore' and '.gitkeep')
+Assert::removeFilesFromDirectory($testDir);
```
-### Remove files from directory
+### Set inaccessible property
```php
assertFileDoesNotExist($dir . '/test.txt');
+$newValue = Assert::inaccessibleProperty($object, 'config');
-rmdir(__DIR__ . '/runtime');
+self::assertSame('test-mode', $newValue);
```
-## Support versions
+## Documentation
+
+For comprehensive testing guidance, see:
-[](https://www.php.net/releases/8.1/en.php)
-[](https://yiiframework.com)
+- 🧪 [Testing Guide](docs/testing.md)
-## Testing
+## Quality code
-[Check the documentation testing](/docs/testing.md) to learn about testing.
+[](https://github.com/php-forge/support/releases)
+[](https://packagist.org/packages/php-forge/support)
+[](https://codecov.io/gh/php-forge/support)
+[](https://github.com/php-forge/support/actions/workflows/static.yml)
+[](https://github.styleci.io/repos/661073468?branch=main)
## Our social networks
-[](https://twitter.com/Terabytesoftw)
+[](https://x.com/Terabytesoftw)
## License
-The MIT License. Please see [License File](LICENSE.md) for more information.
+[](LICENSE.md)
diff --git a/code_coverage/.gitignore b/code_coverage/.gitignore
new file mode 100644
index 0000000..d6b7ef3
--- /dev/null
+++ b/code_coverage/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/composer.json b/composer.json
index 5eda0d1..bb1cb90 100644
--- a/composer.json
+++ b/composer.json
@@ -9,16 +9,18 @@
"testing",
"tests"
],
- "license": "MIT",
+ "license": "BSD-3-Clause",
"require": {
"php": "^8.1",
"phpunit/phpunit": "^10.5"
},
"require-dev": {
+ "infection/infection": "^0.27|^0.31",
"maglnet/composer-require-checker": "^4.7",
- "roave/infection-static-analysis-plugin": "^1.34",
- "symplify/easy-coding-standard": "^12.1",
- "vimeo/psalm": "^5.20"
+ "phpstan/phpstan-strict-rules": "^2.0.3",
+ "symplify/easy-coding-standard": "^12.5",
+ "phpstan/phpstan": "^2.1",
+ "rector/rector": "^2.1"
},
"autoload": {
"psr-4": {
@@ -38,17 +40,17 @@
"config": {
"sort-packages": true,
"allow-plugins": {
- "infection/extension-installer": true
+ "infection/extension-installer": true,
+ "phpstan/extension-installer": true
}
},
"scripts": {
- "check-dependencies": "composer-require-checker",
- "easy-coding-standard": "ecs --fix",
- "mutation": [
- "Composer\\Config::disableProcessTimeout",
- "roave-infection-static-analysis-plugin --threads=2 --only-covered"
- ],
- "psalm": "psalm",
- "test": "phpunit"
+ "check-dependencies": "./vendor/bin/composer-require-checker check",
+ "ecs": "./vendor/bin/ecs --fix",
+ "mutation": "./vendor/bin/infection --threads=4 --ignore-msi-with-no-mutations --only-covered --min-msi=100 --min-covered-msi=100",
+ "mutation-static": "./vendor/bin/infection --threads=4 --ignore-msi-with-no-mutations --only-covered --min-msi=100 --min-covered-msi=100 --static-analysis-tool=phpstan",
+ "rector": "./vendor/bin/rector process src",
+ "static": "./vendor/bin/phpstan --memory-limit=512M",
+ "tests": "./vendor/bin/phpunit"
}
}
diff --git a/docs/testing.md b/docs/testing.md
index b2aa5df..2838bf5 100644
--- a/docs/testing.md
+++ b/docs/testing.md
@@ -4,31 +4,46 @@
This package uses [composer-require-checker](https://github.com/maglnet/ComposerRequireChecker) to check if all dependencies are correctly defined in `composer.json`.
-To run the checker, execute the following command:
+To run the checker, execute the following command.
```shell
composer run check-dependencies
```
+## Easy coding standard
+
+The code is checked with [Easy Coding Standard](https://github.com/easy-coding-standard/easy-coding-standard) and
+[PHP CS Fixer](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer). To run it.
+
+```shell
+composer run ecs
+```
+
## Mutation testing
-Mutation testing is checked with [Infection](https://infection.github.io/). To run it:
+Mutation testing is checked with [Infection](https://infection.github.io/). To run it.
```shell
composer run mutation
```
+With PHPStan analysis, it will also check for static analysis issues during mutation testing.
+
+```shell
+composer run mutation-static
+```
+
## Static analysis
-The code is statically analyzed with [Psalm](https://psalm.dev/). To run static analysis:
+The code is statically analyzed with [PHPStan](https://phpstan.org/). To run static analysis.
```shell
-composer run psalm
+composer run static
```
-## Unit tests
+## Unit Tests
-The code is tested with [PHPUnit](https://phpunit.de/). To run tests:
+The code is tested with [PHPUnit](https://phpunit.de/). To run tests.
```shell
composer run test
diff --git a/ecs.php b/ecs.php
index 78342b8..00781d8 100644
--- a/ecs.php
+++ b/ecs.php
@@ -5,43 +5,74 @@
use PhpCsFixer\Fixer\ClassNotation\ClassDefinitionFixer;
use PhpCsFixer\Fixer\ClassNotation\OrderedClassElementsFixer;
use PhpCsFixer\Fixer\ClassNotation\OrderedTraitsFixer;
+use PhpCsFixer\Fixer\ClassNotation\VisibilityRequiredFixer;
use PhpCsFixer\Fixer\Import\NoUnusedImportsFixer;
+use PhpCsFixer\Fixer\Import\OrderedImportsFixer;
+use PhpCsFixer\Fixer\StringNotation\SingleQuoteFixer;
use Symplify\EasyCodingStandard\Config\ECSConfig;
-use Symplify\EasyCodingStandard\ValueObject\Set\SetList;
-return function (ECSConfig $ecsConfig): void {
- $ecsConfig->paths(
+return ECSConfig::configure()
+ ->withConfiguredRule(
+ ClassDefinitionFixer::class,
+ [
+ 'space_before_parenthesis' => true,
+ ],
+ )
+ ->withConfiguredRule(
+ OrderedClassElementsFixer::class,
+ [
+ 'order' => [
+ 'use_trait',
+ 'constant_public',
+ 'constant_protected',
+ 'constant_private',
+ 'case',
+ 'property_public',
+ 'property_protected',
+ 'property_private',
+ 'construct',
+ 'destruct',
+ 'magic',
+ 'phpunit',
+ 'method_public',
+ 'method_protected',
+ 'method_private',
+ ],
+ 'sort_algorithm' => 'alpha',
+ ],
+ )
+ ->withConfiguredRule(
+ OrderedImportsFixer::class,
+ [
+ 'imports_order' => ['class', 'function', 'const'],
+ 'sort_algorithm' => 'alpha',
+ ],
+ )
+ ->withConfiguredRule(
+ VisibilityRequiredFixer::class,
+ [
+ 'elements' => [],
+ ],
+ )
+ ->withFileExtensions(['php'])
+ ->withPaths(
[
__DIR__ . '/src',
__DIR__ . '/tests',
- ]
- );
-
- // this way you add a single rule
- $ecsConfig->rules(
+ ],
+ )
+ ->withPhpCsFixerSets(perCS20: true)
+ ->withPreparedSets(
+ cleanCode: true,
+ comments: true,
+ docblocks: true,
+ namespaces: true,
+ strict: true,
+ )
+ ->withRules(
[
- OrderedClassElementsFixer::class,
- OrderedTraitsFixer::class,
NoUnusedImportsFixer::class,
+ OrderedTraitsFixer::class,
+ SingleQuoteFixer::class,
]
);
-
- // this way you can add sets - group of rules
- $ecsConfig->sets(
- [
- // run and fix, one by one
- SetList::DOCBLOCK,
- SetList::NAMESPACES,
- SetList::COMMENTS,
- SetList::PSR_12,
- ]
- );
-
- // this way configures a rule
- $ecsConfig->ruleWithConfiguration(
- ClassDefinitionFixer::class,
- [
- 'space_before_parenthesis' => true,
- ],
- );
-};
diff --git a/phpstan.neon b/phpstan.neon
new file mode 100644
index 0000000..63c0f31
--- /dev/null
+++ b/phpstan.neon
@@ -0,0 +1,21 @@
+includes:
+ - phar://phpstan.phar/conf/bleedingEdge.neon
+
+parameters:
+ level: max
+
+ paths:
+ - src
+ - tests
+
+ tmpDir: %currentWorkingDirectory%/runtime
+
+ # Enable strict advanced checks
+ checkImplicitMixed: true
+ checkBenevolentUnionTypes: true
+ checkUninitializedProperties: true
+ checkMissingCallableSignature: true
+ checkTooWideReturnTypesInProtectedAndPublicMethods: true
+ reportAnyTypeWideningInVarTag: true
+ reportPossiblyNonexistentConstantArrayOffset: true
+ reportPossiblyNonexistentGeneralArrayOffset: true
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index e12bbda..e5d7313 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -1,19 +1,24 @@
-
-
-
-
-
-
-
- ./tests
-
-
-
-
- ./src
-
-
- ./vendor
-
-
+
+
+
+
+ tests
+
+
+
+
+
+ ./src
+
+
diff --git a/psalm.xml b/psalm.xml
deleted file mode 100644
index 23bfcce..0000000
--- a/psalm.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/rector.php b/rector.php
new file mode 100644
index 0000000..4a7a938
--- /dev/null
+++ b/rector.php
@@ -0,0 +1,28 @@
+parallel();
+
+ $rectorConfig->importNames();
+
+ $rectorConfig->paths(
+ [
+ __DIR__ . '/src',
+ __DIR__ . '/tests',
+ ],
+ );
+
+ $rectorConfig->sets(
+ [
+ Rector\Set\ValueObject\SetList::PHP_81,
+ Rector\Set\ValueObject\LevelSetList::UP_TO_PHP_81,
+ Rector\Set\ValueObject\SetList::TYPE_DECLARATION,
+ ],
+ );
+
+ $rectorConfig->rule(
+ Rector\CodeQuality\Rector\BooleanAnd\SimplifyEmptyArrayCheckRector::class
+ );
+};
diff --git a/runtime/.gitignore b/runtime/.gitignore
new file mode 100644
index 0000000..d6b7ef3
--- /dev/null
+++ b/runtime/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/src/Assert.php b/src/Assert.php
index 87e26f4..b5ada78 100644
--- a/src/Assert.php
+++ b/src/Assert.php
@@ -4,14 +4,15 @@
namespace PHPForge\Support;
-use PHPUnit\Framework\TestCase;
use ReflectionClass;
+use ReflectionException;
use ReflectionObject;
use RuntimeException;
use function basename;
use function closedir;
use function is_dir;
+use function is_string;
use function opendir;
use function readdir;
use function rmdir;
@@ -19,16 +20,37 @@
use function unlink;
/**
- * @psalm-suppress PropertyNotSetInConstructor
+ * Assertion utility class for advanced test introspection and manipulation.
+ *
+ * Provides static helper methods for accessing and modifying inaccessible properties and methods invoking parent class
+ * logic, and performing file system cleanup in test environments.
+ *
+ * Extends {@see \PHPUnit\Framework\Assert} to offer additional capabilities for testing private/protected members and
+ * for managing test artifacts, supporting robust and isolated unit tests.
+ *
+ * Key features.
+ * - Access and modify inaccessible (private/protected) properties and methods via reflection.
+ * - Invoke parent class methods and properties for testing inheritance scenarios.
+ * - Normalize line endings for cross-platform string assertions.
+ * - Remove files and directories recursively for test environment cleanup.
+ *
+ * @copyright Copyright (C) 2025 PHPForge.
+ * @license https://opensource.org/license/bsd-3-clause BSD 3-Clause License.
*/
-final class Assert extends TestCase
+final class Assert extends \PHPUnit\Framework\Assert
{
/**
- * Asserting two strings equality ignoring line endings.
+ * Asserts that two strings are equal after normalizing line endings to unix style ('\n').
*
- * @param string $expected The expected string.
- * @param string $actual The actual string.
- * @param string $message The message to display if the assertion fails.
+ * Replaces all windows style ('\r\n') line endings with unix style ('\n') in both the expected and actual strings
+ * before performing the equality assertion.
+ *
+ * This ensures cross-platform consistency in string comparisons where line ending differences may otherwise cause
+ * `false` negatives.
+ *
+ * @param string $expected Expected string value, with any line endings.
+ * @param string $actual Actual string value, with any line endings.
+ * @param string $message Optional failure message to display if the assertion fails. Default is an empty string.
*/
public static function equalsWithoutLE(string $expected, string $actual, string $message = ''): void
{
@@ -39,74 +61,142 @@ public static function equalsWithoutLE(string $expected, string $actual, string
}
/**
- * Gets an inaccessible object property.
+ * Retrieves the value of an inaccessible property from a parent class instance.
+ *
+ * Uses reflection to access the specified property of the given parent class, allowing tests to inspect or assert
+ * the value of private or protected properties inherited from parent classes.
+ *
+ * This method is useful for verifying the internal state of objects in inheritance scenarios where direct access
+ * to parent properties is not possible.
+ *
+ * @param object $object Object instance from which to retrieve the property value.
+ * @param object|string $className Name or instance of the parent class containing the property.
+ * @param string $propertyName Name of the property to access.
+ *
+ * @throws ReflectionException
+ *
+ * @return mixed Value of the specified parent property.
+ *
+ * @phpstan-param class-string|object $className
+ */
+ public static function inaccessibleParentProperty(
+ object $object,
+ string|object $className,
+ string $propertyName,
+ ): mixed {
+ $class = new ReflectionClass($className);
+
+ return $class->getProperty($propertyName)->getValue($object);
+ }
+
+ /**
+ * Retrieves the value of an inaccessible property from an object or class instance.
+ *
+ * Uses reflection to access the specified property of the given object or class, allowing tests to inspect or
+ * assert the value of private or protected properties that are otherwise inaccessible.
+ *
+ * This method is useful for verifying the internal state of objects during testing, especially when direct access
+ * to the property is not possible due to visibility constraints.
*
- * @param object $object The object to get the property from.
- * @param string $propertyName The name of the property to get.
+ * @param object|string $object Name of the class or object instance from which to retrieve the property value.
+ * @param string $propertyName Name of the property to access.
+ *
+ * @throws ReflectionException if the property does not exist or is inaccessible.
+ *
+ * @return mixed Value of the specified property, or `null` if the property name is empty.
+ *
+ * @phpstan-param class-string|object $object
*/
- public static function inaccessibleProperty(object $object, string $propertyName): mixed
+ public static function inaccessibleProperty(string|object $object, string $propertyName): mixed
{
$class = new ReflectionClass($object);
- $result = null;
-
if ($propertyName !== '') {
$property = $class->getProperty($propertyName);
-
- /** @psalm-var mixed $result */
- $result = $property->getValue($object);
+ $result = is_string($object) ? $property->getValue() : $property->getValue($object);
}
- return $result;
+ return $result ?? null;
}
/**
- * Invokes an inaccessible method.
+ * Invokes an inaccessible method on the given object instance with the specified arguments.
*
- * @param object $object The object to invoke the method on.
- * @param string $method The name of the method to invoke.
- * @param array $args The arguments to pass to the method.
+ * Uses reflection to access and invoke a private or protected method of the provided object, allowing tests to
+ * execute logic that is not publicly accessible.
+ *
+ * This is useful for verifying internal behavior or side effects during unit testing.
+ *
+ * @param object $object Object instance containing the method to invoke.
+ * @param string $method Name of the method to invoke.
+ * @param array $args Arguments to pass to the method invocation.
+ *
+ * @throws ReflectionException if the method does not exist or is inaccessible.
+ *
+ * @return mixed Value of the invoked method, or `null` if the method name is empty.
+ *
+ * @phpstan-param array $args
*/
public static function invokeMethod(object $object, string $method, array $args = []): mixed
{
$reflection = new ReflectionObject($object);
- $result = null;
-
if ($method !== '') {
$method = $reflection->getMethod($method);
-
- /** @psalm-var mixed $result */
$result = $method->invokeArgs($object, $args);
}
- return $result;
+ return $result ?? null;
}
/**
- * Sets an inaccessible object property to a designated value.
+ * Invokes an inaccessible method from a parent class on the given object instance with the specified arguments.
+ *
+ * Uses reflection to access and invoke a private or protected method defined in the specified parent class,
+ * allowing tests to execute logic that is not publicly accessible from the child class.
+ *
+ * This is useful for verifying inherited behavior or side effects during unit testing of subclasses.
+ *
+ * @param object $object Object instance containing the method to invoke.
+ * @param string $parentClass Name of the parent class containing the method.
+ * @param string $method Name of the method to invoke.
+ * @param array $args Arguments to pass to the method invocation.
+ *
+ * @throws ReflectionException if the method does not exist or is inaccessible in the parent class.
+ *
+ * @return mixed Value of the invoked method, or `null` if the method name is empty.
+ *
+ * @phpstan-param class-string $parentClass
+ * @phpstan-param array $args
*/
- public static function setInaccessibleProperty(
+ public static function invokeParentMethod(
object $object,
- string $propertyName,
- mixed $value
- ): void {
- $class = new ReflectionClass($object);
+ string $parentClass,
+ string $method,
+ array $args = [],
+ ): mixed {
+ $reflection = new ReflectionClass($parentClass);
- if ($propertyName !== '') {
- $property = $class->getProperty($propertyName);
- $property->setValue($object, $value);
+ if ($method !== '') {
+ $method = $reflection->getMethod($method);
+ $result = $method->invokeArgs($object, $args);
}
- unset($class, $property);
+ return $result ?? null;
}
/**
- * Remove files from the directory.
+ * Removes all files and directories recursively from the specified base path, excluding '.gitignore' and
+ * '.gitkeep'.
+ *
+ * Opens the given directory, iterates through its contents, and removes all files and subdirectories except for
+ * special entries ('.', '..', '.gitignore', '.gitkeep').
*
- * @param string $basePath The directory to remove files from.
+ * Subdirectories are processed recursively before removal.
*
- * @throws RuntimeException
+ * @param string $basePath Absolute path to the directory whose contents will be removed.
+ *
+ * @throws RuntimeException if the directory cannot be opened for reading.
*/
public static function removeFilesFromDirectory(string $basePath): void
{
@@ -134,4 +224,67 @@ public static function removeFilesFromDirectory(string $basePath): void
closedir($handle);
}
+
+ /**
+ * Sets the value of an inaccessible property on a parent class instance.
+ *
+ * Uses reflection to assign the specified value to a private or protected property defined in the given parent
+ * class, enabling tests to modify internal state that is otherwise inaccessible due to visibility constraints in
+ * inheritance scenarios.
+ *
+ * This method is useful for testing scenarios that require direct manipulation of parent class internals.
+ *
+ * @param object $object Object instance whose parent property will be set.
+ * @param string $parentClass Name of the parent class containing the property.
+ * @param string $propertyName Name of the property to set.
+ * @param mixed $value Value to assign to the property.
+ *
+ * @throws ReflectionException if the property does not exist or is inaccessible in the parent class.
+ *
+ * @phpstan-param class-string $parentClass
+ */
+ public static function setInaccessibleParentProperty(
+ object $object,
+ string $parentClass,
+ string $propertyName,
+ mixed $value,
+ ): void {
+ $class = new ReflectionClass($parentClass);
+
+ if ($propertyName !== '') {
+ $property = $class->getProperty($propertyName);
+ $property->setValue($object, $value);
+ }
+
+ unset($class, $property);
+ }
+
+ /**
+ * Sets the value of an inaccessible property on the given object instance.
+ *
+ * Uses reflection to assign the specified value to a private or protected property of the provided object enabling
+ * tests to modify internal state that is otherwise inaccessible due to visibility constraints.
+ *
+ * This method is useful for testing scenarios that require direct manipulation of object internals.
+ *
+ * @param object $object Object instance whose property will be set.
+ * @param string $propertyName Name of the property to set.
+ * @param mixed $value Value to assign to the property.
+ *
+ * @throws ReflectionException if the property does not exist or is inaccessible.
+ */
+ public static function setInaccessibleProperty(
+ object $object,
+ string $propertyName,
+ mixed $value,
+ ): void {
+ $class = new ReflectionClass($object);
+
+ if ($propertyName !== '') {
+ $property = $class->getProperty($propertyName);
+ $property->setValue($object, $value);
+ }
+
+ unset($class, $property);
+ }
}
diff --git a/tests/AssertTest.php b/tests/AssertTest.php
index d102a4e..7e90880 100644
--- a/tests/AssertTest.php
+++ b/tests/AssertTest.php
@@ -5,68 +5,153 @@
namespace PHPForge\Support\Tests;
use PHPForge\Support\Assert;
+use PHPForge\Support\Tests\Stub\{TestBaseClass, TestClass};
use PHPUnit\Framework\TestCase;
+use ReflectionException;
use RuntimeException;
/**
- * @psalm-suppress PropertyNotSetInConstructor
+ * Test suite for {@see Assert} utility methods and reflection helpers.
+ *
+ * Verifies the behavior of assertion and reflection-based utility methods for testing inaccessible properties, parent
+ * properties, and methods, as well as file system operations for test directories.
+ *
+ * These tests ensure correct access and mutation of private/protected members, invocation of inaccessible methods, and
+ * robust file removal logic, including error handling for non-existent directories.
+ *
+ * Test coverage.
+ * - Accessing and asserting values of inaccessible properties and parent properties.
+ * - Ensuring correct exception handling for invalid operations.
+ * - Invoking inaccessible methods and parent methods.
+ * - Removing files from directories and handling missing directories.
+ * - Setting values for inaccessible properties and parent properties.
+ *
+ * @copyright Copyright (C) 2025 Terabytesoftw.
+ * @license https://opensource.org/license/bsd-3-clause BSD 3-Clause License.
*/
final class AssertTest extends TestCase
{
- public function testEqualsWithoutLE(): void
+ public function testEqualsWithoutLEReturnsTrueWhenStringsAreIdenticalWithLineEndings(): void
{
- Assert::equalsWithoutLE('foo' . "\r\n" . 'bar', 'foo' . "\r\n" . 'bar');
+ Assert::equalsWithoutLE(
+ "foo\r\nbar",
+ "foo\r\nbar",
+ "Should return 'true' when both strings are identical including line endings.",
+ );
}
- public function testInaccessibleProperty(): void
+ /**
+ * @throws ReflectionException
+ */
+ public function testInaccessibleParentPropertyReturnsExpectedValue(): void
{
- $object = new class () {
- private string $foo = 'bar';
- };
-
- $this->assertSame('bar', Assert::inaccessibleProperty($object, 'foo'));
+ self::assertSame(
+ 'valueParent',
+ Assert::inaccessibleParentProperty(
+ new TestClass(),
+ TestBaseClass::class,
+ 'propertyParent',
+ ),
+ "Should return the value of the parent property 'propertyParent' when accessed via reflection.",
+ );
}
- public function testInvokeMethod(): void
+ public function testRemoveFilesFromDirectoryRemovesAllFiles(): void
{
- $object = new class () {
- protected function foo(): string
- {
- return 'foo';
- }
- };
-
- $this->assertSame('foo', Assert::invokeMethod($object, 'foo'));
+ $dir = dirname(__DIR__) . '/runtime';
+
+ mkdir("{$dir}/subdir");
+ touch("{$dir}/test.txt");
+ touch("{$dir}/subdir/test.txt");
+
+ Assert::removeFilesFromDirectory($dir);
+
+ $this->assertFileDoesNotExist(
+ "{$dir}/test.txt",
+ "File 'test.txt' should not exist after 'removeFilesFromDirectory' method is called.",
+ );
+ $this->assertFileDoesNotExist(
+ "{$dir}/subdir/test.txt",
+ "File 'subdir/test.txt' should not exist after 'removeFilesFromDirectory' method is called.",
+ );
}
- public function testSetInaccessibleProperty(): void
+ /**
+ * @throws ReflectionException
+ */
+ public function testReturnInaccessiblePropertyValueWhenPropertyIsPrivate(): void
{
- $object = new class () {
- private string $foo = 'bar';
- };
+ self::assertSame(
+ 'value',
+ Assert::inaccessibleProperty(new TestClass(), 'property'),
+ "Should return the value of the private property 'property' when accessed via reflection.",
+ );
+ }
- Assert::setInaccessibleProperty($object, 'foo', 'baz');
+ /**
+ * @throws ReflectionException
+ */
+ public function testReturnValueWhenInvokingInaccessibleMethod(): void
+ {
+ $this->assertSame(
+ 'value',
+ Assert::invokeMethod(new TestClass(), 'inaccessibleMethod'),
+ "Should return 'value' when invoking the inaccessible method 'inaccessibleParentMethod' on 'TestClass' " .
+ 'via reflection.',
+ );
+ }
- $this->assertSame('baz', Assert::inaccessibleProperty($object, 'foo'));
+ /**
+ * @throws ReflectionException
+ */
+ public function testReturnValueWhenInvokingInaccessibleParentMethod(): void
+ {
+ $this->assertSame(
+ 'valueParent',
+ Assert::invokeParentMethod(
+ new TestClass(),
+ TestBaseClass::class,
+ 'inaccessibleParentMethod',
+ ),
+ "Should return 'valueParent' when invoking the inaccessible parent method 'inaccessibleParentMethod' on " .
+ "'TestClass' via reflection.",
+ );
}
- public function testRemoveFilesFromDirectory(): void
+ /**
+ * @throws ReflectionException
+ */
+ public function testSetInaccessibleParentProperty(): void
{
- $dir = __DIR__ . '/runtime';
+ $object = new TestClass();
- mkdir($dir);
- mkdir($dir . '/subdir');
- touch($dir . '/test.txt');
- touch($dir . '/subdir/test.txt');
+ Assert::setInaccessibleParentProperty($object, TestBaseClass::class, 'propertyParent', 'foo');
- Assert::removeFilesFromDirectory($dir);
+ $this->assertSame(
+ 'foo',
+ Assert::inaccessibleParentProperty($object, TestBaseClass::class, 'propertyParent'),
+ "Should return 'foo' after setting the parent property 'propertyParent' via " .
+ "'setInaccessibleParentProperty' method.",
+ );
+ }
+
+ /**
+ * @throws ReflectionException
+ */
+ public function testSetInaccessiblePropertySetsValueCorrectly(): void
+ {
+ $object = new TestClass();
- $this->assertFileDoesNotExist($dir . '/test.txt');
+ Assert::setInaccessibleProperty($object, 'property', 'foo');
- rmdir(__DIR__ . '/runtime');
+ $this->assertSame(
+ 'foo',
+ Assert::inaccessibleProperty($object, 'property'),
+ "Should return 'foo' after setting the private property 'property' via 'setInaccessibleProperty' method.",
+ );
}
- public function testRemoveFilesFromDirectoryWithException(): void
+ public function testThrowRuntimeExceptionWhenRemoveFilesFromDirectoryNonExistingDirectory(): void
{
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('Unable to open directory: non-existing-directory');
diff --git a/tests/Stub/TestBaseClass.php b/tests/Stub/TestBaseClass.php
new file mode 100644
index 0000000..8606b59
--- /dev/null
+++ b/tests/Stub/TestBaseClass.php
@@ -0,0 +1,23 @@
+propertyParent;
+ }
+}
diff --git a/tests/Stub/TestClass.php b/tests/Stub/TestClass.php
new file mode 100644
index 0000000..95936f9
--- /dev/null
+++ b/tests/Stub/TestClass.php
@@ -0,0 +1,23 @@
+property;
+ }
+}