Skip to content

Commit

Permalink
Add generators (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
jissereitsma committed Jun 5, 2024
1 parent 6c61c4d commit fbc5b09
Show file tree
Hide file tree
Showing 6 changed files with 328 additions and 0 deletions.
64 changes: 64 additions & 0 deletions Console/Command/GenerateCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php declare(strict_types=1);

namespace Yireo\IntegrationTestHelper\Console\Command;

use Composer\Console\Input\InputArgument;
use Magento\Framework\Component\ComponentRegistrar;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Yireo\IntegrationTestHelper\Check\RedisCheck;
use Yireo\IntegrationTestHelper\Exception\IntegrationTesting\PhpUnitFile\ConstantNotFound;
use Yireo\IntegrationTestHelper\Exception\IntegrationTesting\PhpUnitFile\FileNotFound;
use Yireo\IntegrationTestHelper\Exception\IntegrationTesting\PhpUnitFile\InvalidContent;
use Yireo\IntegrationTestHelper\Generator\TestGenerator;
use Yireo\IntegrationTestHelper\Utilities\CurrentInstallConfig;
use Yireo\IntegrationTestHelper\Check\DbCheck;
use Yireo\IntegrationTestHelper\Utilities\IntegrationTesting\PhpUnitFile\Constant;
use Yireo\IntegrationTestHelper\Check\SearchEngineCheck;

class GenerateCommand extends Command
{
public function __construct(
private TestGenerator $testGenerator,
private ComponentRegistrar $componentRegistrar,
$name = null
) {
parent::__construct($name);
}

/**
* @return void
*/
protected function configure()
{
$this->setName('integration_tests:generate')
->setDescription('Generate tests for a given module')
->addArgument('moduleName', InputArgument::REQUIRED, 'Module name');
}

/**
* @param InputInterface $input
* @param OutputInterface $output
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$moduleName = (string)$input->getArgument('moduleName');
if (empty($moduleName)) {
$output->writeln('<error>No module name given as argument</error>');
return Command::INVALID;
}

$path = $this->componentRegistrar->getPath(ComponentRegistrar::MODULE, $moduleName);
if (false === is_dir($path)) {
$output->writeln('<error>Module name is not registered</error>');
return Command::INVALID;
}

$this->testGenerator->generate($moduleName, $output);

return Command::SUCCESS;
}
}
39 changes: 39 additions & 0 deletions Generator/GenericTestGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php declare(strict_types=1);

namespace Yireo\IntegrationTestHelper\Generator;

use Yireo\IntegrationTestHelper\Test\Integration\Traits\AssertModuleIsEnabled;
use Yireo\IntegrationTestHelper\Test\Integration\Traits\AssertModuleIsRegistered;
use Yireo\IntegrationTestHelper\Test\Integration\Traits\AssertModuleIsRegisteredForReal;
use Yireo\IntegrationTestHelper\Test\Integration\Traits\GetObjectManager;

class GenericTestGenerator
{

public function __construct(
private PhpGeneratorFactory $phpGeneratorFactory,
) {
}

public function generate(string $className, string $classNamePrefix, string $classPath): bool
{
$phpGenerator = $this->phpGeneratorFactory->create($className, $classNamePrefix);
$phpGenerator->addTrait(GetObjectManager::class);


$phpGenerator->addClassMethod('testModule', $this->getMethodModuleTest($moduleName));

$file = 'ModuleTest.php';
return $phpGenerator->generate($classPath . '/' . $file);
}

private function getMethodModuleTest(string $moduleName): string
{
return <<<EOF
\$moduleName = '$moduleName';
\$this->assertModuleIsEnabled(\$moduleName);
\$this->assertModuleIsRegistered(\$moduleName);
\$this->assertModuleIsRegisteredForReal(\$moduleName);
EOF;
}
}
37 changes: 37 additions & 0 deletions Generator/ModuleTestGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php declare(strict_types=1);

namespace Yireo\IntegrationTestHelper\Generator;

use Yireo\IntegrationTestHelper\Test\Integration\Traits\AssertModuleIsEnabled;
use Yireo\IntegrationTestHelper\Test\Integration\Traits\AssertModuleIsRegistered;
use Yireo\IntegrationTestHelper\Test\Integration\Traits\AssertModuleIsRegisteredForReal;

class ModuleTestGenerator
{
public function __construct(
private PhpGeneratorFactory $phpGeneratorFactory,
) {
}

public function generate(string $moduleName, string $classNamePrefix, string $classPath): bool
{
$phpGenerator = $this->phpGeneratorFactory->create('ModuleTest', $classNamePrefix);
$phpGenerator->addTrait(AssertModuleIsEnabled::class);
$phpGenerator->addTrait(AssertModuleIsRegistered::class);
$phpGenerator->addTrait(AssertModuleIsRegisteredForReal::class);
$phpGenerator->addClassMethod('testModule', $this->getMethodModuleTest($moduleName));

$file = 'ModuleTest.php';
return $phpGenerator->generate($classPath . '/' . $file);
}

private function getMethodModuleTest(string $moduleName): string
{
return <<<EOF
\$moduleName = '$moduleName';
\$this->assertModuleIsEnabled(\$moduleName);
\$this->assertModuleIsRegistered(\$moduleName);
\$this->assertModuleIsRegisteredForReal(\$moduleName);
EOF;
}
}
44 changes: 44 additions & 0 deletions Generator/PhpGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php declare(strict_types=1);

namespace Yireo\IntegrationTestHelper\Generator;

use Magento\Framework\Filesystem\Directory\WriteInterface;
use Nette\PhpGenerator\ClassType;
use Nette\PhpGenerator\PhpFile;
use Nette\PhpGenerator\PhpNamespace;
use Nette\PhpGenerator\PsrPrinter;

class PhpGenerator
{
public function __construct(
private ClassType $classType,
private PhpNamespace $namespace,
private PhpFile $file,
private WriteInterface $writer
) {
}

public function addClassMethod(string $methodName, string $methodBody)
{
$this->classType->addMethod($methodName)
->setFinal()
->setPublic()
->setBody($methodBody);
}

public function addTrait(string $traitName)
{
$this->classType->addTrait($traitName);
$this->namespace->addUse($traitName);
}

public function generate(string $file):bool
{
$this->namespace->add($this->classType);
$this->file->addNamespace($this->namespace);

$contents = (new PsrPrinter)->printFile($this->file);
$this->writer->writeFile($file, $contents);
return true;
}
}
36 changes: 36 additions & 0 deletions Generator/PhpGeneratorFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php declare(strict_types=1);

namespace Yireo\IntegrationTestHelper\Generator;

use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\Filesystem;
use Nette\PhpGenerator\ClassType;
use Nette\PhpGenerator\PhpFile;
use Nette\PhpGenerator\PhpNamespace;
use PHPUnit\Framework\TestCase;

class PhpGeneratorFactory
{
public function __construct(
private DirectoryList $directoryList,
private Filesystem $filesystem
) {
}

public function create(string $className, string $classNamespace): PHPGenerator
{
$classType = new ClassType($className);
$classType->setFinal();
$classType->setExtends(TestCase::class);

$namespaceType = new PhpNamespace($classNamespace);
$namespaceType->add($classType);
$namespaceType->addUse(TestCase::class);

$fileType = new PhpFile;
$fileType->setStrictTypes();

$writer = $this->filesystem->getDirectoryWrite($this->directoryList::ROOT);
return new PhpGenerator($classType, $namespaceType, $fileType, $writer);
}
}
108 changes: 108 additions & 0 deletions Generator/TestGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<?php declare(strict_types=1);

namespace Yireo\IntegrationTestHelper\Generator;

use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\Component\ComponentRegistrar;
use Magento\Framework\Filesystem;
use PhpToken;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use RecursiveRegexIterator;
use RegexIterator;
use Symfony\Component\Console\Output\OutputInterface;

class TestGenerator
{
public function __construct(
private ComponentRegistrar $componentRegistrar,
private ModuleTestGenerator $moduleTestGenerator,
private GenericTestGenerator $genericTestGenerator,
private DirectoryList $directoryList,
private Filesystem $filesystem
) {
}

public function generate(string $moduleName, OutputInterface $output)
{
$modulePath = $this->componentRegistrar->getPath(ComponentRegistrar::MODULE, $moduleName);
$this->createFolder($modulePath.'/Test/Integration/');

$classPath = $modulePath.'/Test/Integration/';
$classNamePrefix = $this->getClassNamePrefix($moduleName);

$output->writeln('Generating module test in '.$classPath);
$this->moduleTestGenerator->generate($moduleName, $classNamePrefix, $classPath);

$phpClasses = $this->getPhpClasses($modulePath);
print_r($phpClasses);
exit;
foreach ($phpClasses as $phpClass) {
$this->genericTestGenerator->generate($phpClass, $classNamePrefix, $classPath);
}
}

private function getClassNamePrefix(string $moduleName): string
{
$moduleNameParts = explode('_', $moduleName);

return $moduleNameParts[0].'\\'.$moduleNameParts[1].'\\Test\\Integration';
}

private function createFolder(string $folder)
{
$writer = $this->filesystem->getDirectoryWrite($this->directoryList::ROOT);
$writer->create($folder);
}

private function getPhpClasses(string $folder): array
{
$classes = [];
$directory = new RecursiveDirectoryIterator($folder);
$iterator = new RecursiveIteratorIterator($directory);
$regex = new RegexIterator($iterator, '/^.+\.php$/i', RecursiveRegexIterator::GET_MATCH);

foreach ($regex as $file) {
$file = $file[0];
$classes[$file] = $this->getClassFromFile((string)$file);
}

print_r($classes);exit;

return $classes;
}

private function getClassFromFile(string $file): array
{
$classes = [];
$namespace = '';
$tokens = PhpToken::tokenize(file_get_contents($file));

for ($i = 0; $i < count($tokens); $i++) {
if ($tokens[$i]->getTokenName() === 'T_NAMESPACE') {
for ($j = $i + 1; $j < count($tokens); $j++) {
if ($tokens[$j]->getTokenName() === 'T_NAME_QUALIFIED') {
$namespace = $tokens[$j]->text;
break;
}
}
}

if ($tokens[$i]->getTokenName() === 'T_CLASS') {
for ($j = $i + 1; $j < count($tokens); $j++) {
if ($tokens[$j]->getTokenName() === 'T_WHITESPACE') {
continue;
}

if ($tokens[$j]->getTokenName() === 'T_STRING') {
$classes[] = $namespace . '\\' . $tokens[$j]->text;
} else {
break;
}
}
}
}
return $classes;

}
}

0 comments on commit fbc5b09

Please sign in to comment.