Skip to content

Commit

Permalink
Merge pull request #4 from evktalo/feature/magento-2-4-4
Browse files Browse the repository at this point in the history
Support for PHP 8 and PHPUnit 9
  • Loading branch information
olmer authored May 2, 2022
2 parents f1409f2 + 024e400 commit 3bbfe78
Show file tree
Hide file tree
Showing 12 changed files with 540 additions and 85 deletions.
46 changes: 9 additions & 37 deletions Code/Generator.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
Code\Generator\Io,
Filesystem\Driver\File as Reader
};
use Olmer\UnitTestsGenerator\Code\Generator\ClassNameParser;

class Generator
{
Expand All @@ -23,6 +24,10 @@ class Generator
* @var Reader
*/
private $reader;
/**
* @var ClassNameParser
*/
private $classNameParser;

/**
* Generator constructor.
Expand All @@ -34,11 +39,13 @@ class Generator
public function __construct(
Generator\UnitTestFactory $testGenerator,
Io $ioObject,
Reader $reader
Reader $reader,
ClassNameParser $classNameParser
) {
$this->testGeneratorFactory = $testGenerator;
$this->ioObject = $ioObject;
$this->reader = $reader;
$this->classNameParser = $classNameParser;
}

/**
Expand Down Expand Up @@ -81,41 +88,6 @@ private function getClassName(string $path): string
}

$fileContents = $this->reader->fileGetContents($path);
return $this->parseClassName($fileContents);
}

/**
* @param string $content
*
* @return string
*/
private function parseClassName(string $content): string
{
$class = $namespace = '';
$i = 0;
$tokens = \token_get_all($content);
$tokensCount = \count($tokens);
for (; $i < $tokensCount; $i++) {
if ($tokens[$i][0] === T_NAMESPACE) {
for ($j = $i + 1; $j < $tokensCount; $j++) {
if ($tokens[$j][0] === T_STRING) {
$namespace .= '\\' . $tokens[$j][1];
} else {
if ($tokens[$j] === '{' || $tokens[$j] === ';') {
break;
}
}
}
}
if ($tokens[$i][0] === T_CLASS) {
for ($j = $i + 1; $j < $tokensCount; $j++) {
if ($tokens[$j] === '{') {
$class = '\\' . $tokens[$i + 2][1];
break 2;
}
}
}
}
return $namespace . $class;
return $this->classNameParser->parseClassName($fileContents);
}
}
45 changes: 45 additions & 0 deletions Code/Generator/ClassNameParser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

namespace Olmer\UnitTestsGenerator\Code\Generator;

class ClassNameParser
{
/**
* @param string $content
*
* @return string
*/
public function parseClassName(string $content): string
{
$class = $namespace = '';
$i = 0;
$tokens = \token_get_all($content);
$tokensCount = \count($tokens);
for (; $i < $tokensCount; $i++) {
if ($tokens[$i][0] === T_NAMESPACE) {
for ($j = $i + 1; $j < $tokensCount; $j++) {
// T_NAME_QUALIFIED for PHP > 8.0, T_STRING as fallback
$qualifiedNameToken = defined('T_NAME_QUALIFIED') ? T_NAME_QUALIFIED : T_STRING;
if ($tokens[$j][0] === $qualifiedNameToken) {
$namespace .= '\\' . $tokens[$j][1];
} else {
if ($tokens[$j] === '{' || $tokens[$j] === ';') {
break;
}
}
}
}
if ($tokens[$i][0] === T_CLASS) {
for ($j = $i + 1; $j < $tokensCount; $j++) {
if ($tokens[$j] === '{') {
$class = '\\' . $tokens[$i + 2][1];
break 2;
}
}
}
}
return $namespace . $class;
}
}
109 changes: 71 additions & 38 deletions Code/Generator/UnitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@

namespace Olmer\UnitTestsGenerator\Code\Generator;

use Magento\Framework\Code\Generator\DefinedClasses;
use Magento\Framework\Code\Generator\Io;
use \Magento\Framework\Code\Generator\CodeGeneratorInterface;
use Olmer\UnitTestsGenerator\Code\Generator\UnitTest\ConstructorArgumentsResolver;
use Olmer\UnitTestsGenerator\Code\Generator\UnitTest\SetupMethodBuilder;
use Olmer\UnitTestsGenerator\Code\Generator\UnitTest\TestObjectCreationBuilder;

class UnitTest extends \Magento\Framework\Code\Generator\EntityAbstract
{
/**
Expand All @@ -18,6 +25,34 @@ class UnitTest extends \Magento\Framework\Code\Generator\EntityAbstract
* @var array
*/
private $constructorArguments;
/**
* @var ConstructorArgumentsResolver
*/
private $constructorArgumentsResolver;
/**
* @var SetupMethodBuilder
*/
private $setupMethodBuilder;
/**
* @var TestObjectCreationBuilder
*/
private $testObjectCreationBuilder;

public function __construct(
ConstructorArgumentsResolver $constructorArgumentsResolver,
SetupMethodBuilder $setupMethodBuilder,
TestObjectCreationBuilder $testObjectCreationBuilder,
$sourceClassName = null,
$resultClassName = null,
Io $ioObject = null,
CodeGeneratorInterface $classGenerator = null,
DefinedClasses $definedClasses = null
) {
$this->constructorArgumentsResolver = $constructorArgumentsResolver;
$this->setupMethodBuilder = $setupMethodBuilder;
$this->testObjectCreationBuilder = $testObjectCreationBuilder;
parent::__construct($sourceClassName, $resultClassName, $ioObject, $classGenerator, $definedClasses);
}

/**
* @return \ReflectionClass
Expand Down Expand Up @@ -53,10 +88,10 @@ protected function _getClassProperties()
'visibility' => 'private',
'docblock' => [
'shortDescription' => "Mock {$e['name']}",
'tags' => [['name' => 'var', 'description' => '\\' . "{$e['class']}|PHPUnit_Framework_MockObject_MockObject"]],
'tags' => [['name' => 'var', 'description' => '\\' . "{$e['class']}|" . \PHPUnit\Framework\MockObject\MockObject::class]],
],
];
}, $this->getConstructorArgumentsWithFactoryInstances());
}, $this->getConstructorArgumentsWithFactoryInstances($this->getConstructorArguments()));

$properties[] = [
'name' => 'objectManager',
Expand Down Expand Up @@ -96,16 +131,16 @@ protected function _getDefaultConstructorDefinition()
*/
protected function _getClassMethods()
{
$setUp = [
'name' => 'setUp',
'parameters' => [],
'body' => "\$this->objectManager = new ObjectManager(\$this);\n"
. \implode("\n", $this->getSetupMethodParamsDefinition()) . "\n"
. $this->getTestObjectCreation(),
'docblock' => [
'shortDescription' => 'Main set up method',
],
];
$setupMethodParamsDefinition = $this->getSetupMethodParamsDefinition(
$this->getConstructorArgumentsWithFactoryInstances(
$this->getConstructorArguments()
)
);
$testObjectCreationCode = $this->testObjectCreationBuilder->build(
$this->getSourceClassName(),
$this->getObjectCreationParams($this->getConstructorArguments())
);
$setUp = $this->setupMethodBuilder->build($setupMethodParamsDefinition, $testObjectCreationCode);

return \array_merge([$setUp], $this->getTestMethodsWithProviders());
}
Expand All @@ -122,12 +157,14 @@ protected function _generateCode()
}

/**
* @param array $arguments
*
* @return array
*/
private function getSetupMethodParamsDefinition(): array
private function getSetupMethodParamsDefinition(array $arguments): array
{
$result = [];
foreach ($this->getConstructorArgumentsWithFactoryInstances() as $argument) {
foreach ($arguments as $argument) {
$result[] = "\$this->{$argument['name']}"
. " = \$this->createMock(\\" . "{$argument['class']}::class);";
if (\preg_match('/\w+Factory$/', $argument['class']) === 1) {
Expand All @@ -141,64 +178,60 @@ private function getSetupMethodParamsDefinition(): array
}

/**
* @param string $sourceClassName
* @param array $objectCreationParams
*
* @return string
*/
private function getTestObjectCreation(): string
{
private function getTestObjectCreation(
string $sourceClassName,
array $objectCreationParams
): string {
return "\$this->testObject = \$this->objectManager->getObject(\n"
. $this->getSourceClassName() . "::class,\n"
. $sourceClassName . "::class,\n"
. " [\n"
. \implode("\n", $this->getObjectCreationParams())
. \implode("\n", $objectCreationParams)
. "\n ]\n"
. ");";
}

/**
* @param array $constructorArguments
*
* @return array
*/
private function getObjectCreationParams(): array
private function getObjectCreationParams(array $constructorArguments): array
{
return \array_map(function ($e) {
return " '{$e['name']}' => \$this->{$e['name']},";
}, $this->getConstructorArguments());
}, $constructorArguments);
}

/**
* @param string $className
*
* @return array
*/
private function getConstructorArguments(): array
{
if ($this->constructorArguments === null) {
$this->constructorArguments = [];

try {
$method = $this->getSourceReflectionClass()->getMethod('__construct');
foreach ($method->getParameters() as $parameter) {
if (!$parameter->getClass()) {
continue;
}
$this->constructorArguments[] = [
'name' => $parameter->getName(),
'class' => $parameter->getClass()->getName()
];
}
$constructor = $this->getSourceReflectionClass()->getMethod('__construct');
$this->constructorArguments = $this->constructorArgumentsResolver->resolve($constructor);
} catch (\ReflectionException $e) {
return $this->constructorArguments;
$this->constructorArguments = [];
}
}

return $this->constructorArguments;
}

/**
* @param array $arguments
*
* @return array
*/
private function getConstructorArgumentsWithFactoryInstances(): array
private function getConstructorArgumentsWithFactoryInstances(array $arguments): array
{
$result = [];
$arguments = $this->getConstructorArguments();
foreach ($arguments as $argument) {
if (\preg_match('/\w+Factory$/', $argument['class']) === 1) {
$result[] = [
Expand Down Expand Up @@ -311,7 +344,7 @@ private function addDefaultUses()
\PHPUnit\Framework\TestCase::class
);
$this->_classGenerator->addUse(
\PHPUnit_Framework_MockObject_MockObject::class
\PHPUnit\Framework\MockObject\MockObject::class
);
}

Expand Down
34 changes: 34 additions & 0 deletions Code/Generator/UnitTest/ConstructorArgumentsResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace Olmer\UnitTestsGenerator\Code\Generator\UnitTest;

class ConstructorArgumentsResolver
{
/**
* @param \ReflectionMethod $constructor
*
* @return array
*/
public function resolve(\ReflectionMethod $constructor): array
{
$constructorArguments = [];

try {
foreach ($constructor->getParameters() as $parameter) {
if (!$parameter->getType()) {
continue;
}
$constructorArguments[] = [
'name' => $parameter->getName(),
'class' => $parameter->getType()->getName()
];
}
} catch (\ReflectionException $e) {
return $constructorArguments;
}

return $constructorArguments;
}
}
29 changes: 29 additions & 0 deletions Code/Generator/UnitTest/SetupMethodBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace Olmer\UnitTestsGenerator\Code\Generator\UnitTest;

class SetupMethodBuilder
{
/**
* @param array $setupMethodParamsDefinition
* @param string $testObjectCreationCode
*
* @return array
*/
public function build(array $setupMethodParamsDefinition, string $testObjectCreationCode): array
{
return [
'name' => 'setUp',
'parameters' => [],
'body' => "\$this->objectManager = new ObjectManager(\$this);\n"
. \implode("\n", $setupMethodParamsDefinition) . "\n"
. $testObjectCreationCode,
'docblock' => [
'shortDescription' => 'Main set up method',
],
'returnType' => 'void'
];
}
}
Loading

0 comments on commit 3bbfe78

Please sign in to comment.