diff --git a/src/Builder/FileNormalizer/CodeSniffStyleFileNormalizer.php b/src/Builder/FileNormalizer/CodeSniffStyleFileNormalizer.php index 2ae0db8b..e3ca3114 100644 --- a/src/Builder/FileNormalizer/CodeSniffStyleFileNormalizer.php +++ b/src/Builder/FileNormalizer/CodeSniffStyleFileNormalizer.php @@ -9,9 +9,7 @@ namespace SprykerSdk\Integrator\Builder\FileNormalizer; -use RuntimeException; use SprykerSdk\Integrator\IntegratorConfig; -use SprykerSdk\Utils\Infrastructure\Service\ProcessRunnerServiceInterface; class CodeSniffStyleFileNormalizer implements FileNormalizerInterface { @@ -31,18 +29,18 @@ class CodeSniffStyleFileNormalizer implements FileNormalizerInterface protected $config; /** - * @var \SprykerSdk\Utils\Infrastructure\Service\ProcessRunnerServiceInterface + * @var \SprykerSdk\Integrator\Builder\FileNormalizer\CodeSnifferCommandExecutor */ - protected ProcessRunnerServiceInterface $processRunner; + protected CodeSnifferCommandExecutor $codeSnifferCommandExecutor; /** * @param \SprykerSdk\Integrator\IntegratorConfig $config - * @param \SprykerSdk\Utils\Infrastructure\Service\ProcessRunnerServiceInterface $processRunner + * @param \SprykerSdk\Integrator\Builder\FileNormalizer\CodeSnifferCommandExecutor $codeSnifferCommandExecutor */ - public function __construct(IntegratorConfig $config, ProcessRunnerServiceInterface $processRunner) + public function __construct(IntegratorConfig $config, CodeSnifferCommandExecutor $codeSnifferCommandExecutor) { $this->config = $config; - $this->processRunner = $processRunner; + $this->codeSnifferCommandExecutor = $codeSnifferCommandExecutor; } /** @@ -64,19 +62,15 @@ public function getErrorMessage(): ?string /** * @param array $filePaths * - * @throws \RuntimeException - * * @return void */ public function normalize(array $filePaths): void { $projectConsolePath = $this->getProjectConsolePath(); foreach ($this->getProjectRelativeFilePaths($filePaths) as $filePath) { - $process = $this->processRunner->run([$projectConsolePath, static::PHP_CS_FIX_COMMAND, $filePath]); - - if ($process->getExitCode() > 0 && $process->getErrorOutput() !== '') { - throw new RuntimeException($process->getErrorOutput()); - } + $this->codeSnifferCommandExecutor->executeCodeSnifferCommand( + [$projectConsolePath, static::PHP_CS_FIX_COMMAND, $filePath], + ); } } diff --git a/src/Builder/FileNormalizer/CodeSnifferCommandExecutor.php b/src/Builder/FileNormalizer/CodeSnifferCommandExecutor.php new file mode 100644 index 00000000..424b75aa --- /dev/null +++ b/src/Builder/FileNormalizer/CodeSnifferCommandExecutor.php @@ -0,0 +1,80 @@ +processRunner = $processRunner; + } + + /** + * @param array $command + * + * @return void + */ + public function executeCodeSnifferCommand(array $command): void + { + $leftAttempts = static::EXECUTION_ATTEMPTS; + + while ($leftAttempts > 0) { + $process = $this->processRunner->run($command); + + --$leftAttempts; + + if ($process->getExitCode() === static::SUCCESS_COMMAND_CODE) { + break; + } + + if ($leftAttempts === 0 && $process->getExitCode() !== static::SUCCESS_COMMAND_CODE) { + $this->throwFailedExecutionException($process); + } + } + } + + /** + * @param \Symfony\Component\Process\Process $process + * + * @throws \RuntimeException + * + * @return void + */ + protected function throwFailedExecutionException(Process $process): void + { + $errorOutput = $process->getErrorOutput(); + + if ($errorOutput !== '') { + throw new RuntimeException($errorOutput); + } + } +} diff --git a/src/Builder/FileNormalizer/PhpCSFixerFileNormalizer.php b/src/Builder/FileNormalizer/PhpCSFixerFileNormalizer.php index f7e450d8..27af7283 100644 --- a/src/Builder/FileNormalizer/PhpCSFixerFileNormalizer.php +++ b/src/Builder/FileNormalizer/PhpCSFixerFileNormalizer.php @@ -9,9 +9,7 @@ namespace SprykerSdk\Integrator\Builder\FileNormalizer; -use RuntimeException; use SprykerSdk\Integrator\IntegratorConfig; -use SprykerSdk\Utils\Infrastructure\Service\ProcessRunnerServiceInterface; class PhpCSFixerFileNormalizer implements FileNormalizerInterface { @@ -26,18 +24,18 @@ class PhpCSFixerFileNormalizer implements FileNormalizerInterface protected $config; /** - * @var \SprykerSdk\Utils\Infrastructure\Service\ProcessRunnerServiceInterface + * @var \SprykerSdk\Integrator\Builder\FileNormalizer\CodeSnifferCommandExecutor */ - protected ProcessRunnerServiceInterface $processRunner; + protected CodeSnifferCommandExecutor $codeSnifferCommandExecutor; /** * @param \SprykerSdk\Integrator\IntegratorConfig $config - * @param \SprykerSdk\Utils\Infrastructure\Service\ProcessRunnerServiceInterface $processRunner + * @param \SprykerSdk\Integrator\Builder\FileNormalizer\CodeSnifferCommandExecutor $codeSnifferCommandExecutor */ - public function __construct(IntegratorConfig $config, ProcessRunnerServiceInterface $processRunner) + public function __construct(IntegratorConfig $config, CodeSnifferCommandExecutor $codeSnifferCommandExecutor) { $this->config = $config; - $this->processRunner = $processRunner; + $this->codeSnifferCommandExecutor = $codeSnifferCommandExecutor; } /** @@ -59,26 +57,13 @@ public function getErrorMessage(): ?string /** * @param array $filePaths * - * @throws \RuntimeException - * * @return void */ public function normalize(array $filePaths): void { - $command = [$this->getCSFixPath(), ...$this->getAbsoluteFilePaths($filePaths)]; - $process = $this->processRunner->run($command); - - // TODO remove when phpcbf will be able to fix all issues in file during the one iteration - if (defined('TEST_INTEGRATOR_MODE') && TEST_INTEGRATOR_MODE === 'true' && $process->getExitCode() !== 0) { - $process = $this->processRunner->run($command); - if ($process->getExitCode() !== 0) { - $process = $this->processRunner->run($command); - } - } - - if ($process->getExitCode() > 0 && $process->getErrorOutput() !== '') { - throw new RuntimeException($process->getErrorOutput()); - } + $this->codeSnifferCommandExecutor->executeCodeSnifferCommand( + [$this->getCSFixPath(), ...$this->getAbsoluteFilePaths($filePaths)], + ); } /** diff --git a/src/IntegratorFactory.php b/src/IntegratorFactory.php index 04da197f..2a5f686b 100644 --- a/src/IntegratorFactory.php +++ b/src/IntegratorFactory.php @@ -93,6 +93,7 @@ use SprykerSdk\Integrator\Builder\Extractor\ExpressionExtractorInterface; use SprykerSdk\Integrator\Builder\Extractor\ValueExtractor\ValueExtractorStrategyCollection; use SprykerSdk\Integrator\Builder\FileBuilderFacade; +use SprykerSdk\Integrator\Builder\FileNormalizer\CodeSnifferCommandExecutor; use SprykerSdk\Integrator\Builder\FileNormalizer\CodeSnifferCompositeNormalizer; use SprykerSdk\Integrator\Builder\FileNormalizer\CodeSniffStyleFileNormalizer; use SprykerSdk\Integrator\Builder\FileNormalizer\FileNormalizerInterface; @@ -597,7 +598,7 @@ public function createCodeSnifferCompositeNormalizer(): FileNormalizerInterface */ public function createCodeSniffStyleFileNormalizer(): FileNormalizerInterface { - return new CodeSniffStyleFileNormalizer($this->getConfig(), $this->createProcessRunnerService()); + return new CodeSniffStyleFileNormalizer($this->getConfig(), $this->createCodeSnifferCommandExecutor()); } /** @@ -605,7 +606,7 @@ public function createCodeSniffStyleFileNormalizer(): FileNormalizerInterface */ public function createPhpCSFixerNormalizer(): FileNormalizerInterface { - return new PhpCSFixerFileNormalizer($this->getConfig(), $this->createProcessRunnerService()); + return new PhpCSFixerFileNormalizer($this->getConfig(), $this->createCodeSnifferCommandExecutor()); } /** @@ -616,6 +617,14 @@ public function createProcessRunnerService(): ProcessRunnerServiceInterface return new ProcessRunnerService(); } + /** + * @return \SprykerSdk\Integrator\Builder\FileNormalizer\CodeSnifferCommandExecutor + */ + public function createCodeSnifferCommandExecutor(): CodeSnifferCommandExecutor + { + return new CodeSnifferCommandExecutor($this->createProcessRunnerService()); + } + /** * @return \SprykerSdk\Integrator\Builder\Printer\ClassDiffPrinterInterface */ diff --git a/tests/SprykerSdkTest/Integrator/Builder/FileNormalizer/CodeSniffStyleFileNormalizerTest.php b/tests/SprykerSdkTest/Integrator/Builder/FileNormalizer/CodeSniffStyleFileNormalizerTest.php index d218925b..f2fb7d15 100644 --- a/tests/SprykerSdkTest/Integrator/Builder/FileNormalizer/CodeSniffStyleFileNormalizerTest.php +++ b/tests/SprykerSdkTest/Integrator/Builder/FileNormalizer/CodeSniffStyleFileNormalizerTest.php @@ -10,13 +10,10 @@ namespace SprykerSdkTest\Integrator\Builder\FileNormalizer; use PHPUnit\Framework\TestCase; -use RuntimeException; +use SprykerSdk\Integrator\Builder\FileNormalizer\CodeSnifferCommandExecutor; use SprykerSdk\Integrator\Builder\FileNormalizer\CodeSniffStyleFileNormalizer; use SprykerSdk\Integrator\Builder\FileStorage\FileStorage; use SprykerSdk\Integrator\IntegratorConfig; -use SprykerSdk\Utils\Infrastructure\Service\ProcessRunnerService; -use SprykerSdk\Utils\Infrastructure\Service\ProcessRunnerServiceInterface; -use Symfony\Component\Process\Process; class CodeSniffStyleFileNormalizerTest extends TestCase { @@ -26,16 +23,9 @@ class CodeSniffStyleFileNormalizerTest extends TestCase public function testExecuteSuccess(): void { // Arrange - $processExecutorMock = $this->createProcessExecutorMock(0, ''); - $processExecutorMock->expects($this->once())->method('run')->with( - $this->callback(function ($command) { - return $command[1] === 'code:sniff:style -f'; - }), - ); - - // Arrange + $codesnifferCommandExecutor = $this->createCodeSnifferCommandExecutorMock(); $configMock = $this->createMock(IntegratorConfig::class); - $normalizer = new CodeSniffStyleFileNormalizer($configMock, $processExecutorMock); + $normalizer = new CodeSniffStyleFileNormalizer($configMock, $codesnifferCommandExecutor); $fileStorage = new FileStorage(); $fileStorage->addFile('someClass.php'); @@ -47,15 +37,13 @@ public function testExecuteSuccess(): void /** * @return void */ - public function testExecuteFailed(): void + public function testExecuteSuccessWhenProjectDirIsSet(): void { - // Assert - $this->expectException(RuntimeException::class); - // Arrange - $processExecutorMock = $this->createProcessExecutorMock(2, 'process error'); + $codesnifferCommandExecutor = $this->createCodeSnifferCommandExecutorMock(); $configMock = $this->createMock(IntegratorConfig::class); - $normalizer = new CodeSniffStyleFileNormalizer($configMock, $processExecutorMock); + $configMock->method('getProjectRootDirectory')->willReturn('projectRoot'); + $normalizer = new CodeSniffStyleFileNormalizer($configMock, $codesnifferCommandExecutor); $fileStorage = new FileStorage(); $fileStorage->addFile('someClass.php'); @@ -67,47 +55,52 @@ public function testExecuteFailed(): void /** * @return void */ - public function testGetErrorMessageShouldReturnErrorMessage(): void + public function testExecuteSuccessWhenProjectDirIsSetAndHasSubPath(): void { // Arrange - $processExecutorMock = $this->createProcessExecutorMock(2, 'process error'); + $codesnifferCommandExecutor = $this->createCodeSnifferCommandExecutorMock(); $configMock = $this->createMock(IntegratorConfig::class); - $normalizer = new CodeSniffStyleFileNormalizer($configMock, $processExecutorMock); + $configMock->method('getProjectRootDirectory')->willReturn('projectDir'); + $normalizer = new CodeSniffStyleFileNormalizer($configMock, $codesnifferCommandExecutor); - // Act - $errorMessage = $normalizer->getErrorMessage(); + $fileStorage = new FileStorage(); + $fileStorage->addFile('projectDir/someClass.php'); - // Assert - $this->assertNull($errorMessage); + // Act + $normalizer->normalize($fileStorage->flush()); } /** - * @param int $exitCode - * @param string $errorOutput - * - * @return \SprykerSdk\Utils\Infrastructure\Service\ProcessRunnerServiceInterface + * @return void */ - protected function createProcessExecutorMock(int $exitCode, string $errorOutput = ''): ProcessRunnerServiceInterface + public function testGetErrorMessageShouldReturnErrorMessage(): void { - $processMock = $this->createProcessMock($exitCode, $errorOutput); - $processExecutorMock = $this->createMock(ProcessRunnerService::class); - $processExecutorMock->method('run')->willReturn($processMock); + // Arrange + $codesnifferCommandExecutor = $this->createMock(CodeSnifferCommandExecutor::class); + $configMock = $this->createMock(IntegratorConfig::class); + $normalizer = new CodeSniffStyleFileNormalizer($configMock, $codesnifferCommandExecutor); - return $processExecutorMock; + // Act + $errorMessage = $normalizer->getErrorMessage(); + + // Assert + $this->assertNull($errorMessage); } /** - * @param int $exitCode - * @param string $errorOutput - * - * @return \Symfony\Component\Process\Process + * @return \SprykerSdk\Integrator\Builder\FileNormalizer\CodeSnifferCommandExecutor */ - protected function createProcessMock(int $exitCode, string $errorOutput = ''): Process + protected function createCodeSnifferCommandExecutorMock(): CodeSnifferCommandExecutor { - $processMock = $this->createMock(Process::class); - $processMock->method('getExitCode')->willReturn($exitCode); - $processMock->method('getErrorOutput')->willReturn($errorOutput); - - return $processMock; + $codeSnifferCommandExecutor = $this->createMock(CodeSnifferCommandExecutor::class); + $codeSnifferCommandExecutor->expects($this->once()) + ->method('executeCodeSnifferCommand') + ->with( + $this->callback(function ($command) { + return $command[1] === 'code:sniff:style -f'; + }), + ); + + return $codeSnifferCommandExecutor; } } diff --git a/tests/SprykerSdkTest/Integrator/Builder/FileNormalizer/CodeSnifferCommandExecutorTest.php b/tests/SprykerSdkTest/Integrator/Builder/FileNormalizer/CodeSnifferCommandExecutorTest.php new file mode 100644 index 00000000..f5081616 --- /dev/null +++ b/tests/SprykerSdkTest/Integrator/Builder/FileNormalizer/CodeSnifferCommandExecutorTest.php @@ -0,0 +1,98 @@ +createProcessRunnerServiceMock($executionCount, $this->createProcessMock(0, '')), + ); + + // Act + $codeSnifferCommandExecutor->executeCodeSnifferCommand([]); + } + + /** + * @return void + */ + public function testExecuteCodeSnifferCommandShouldSilentlyFailWhenNoErrorOutput(): void + { + // Arrange + $codeSnifferCommandExecutor = new CodeSnifferCommandExecutor( + $this->createProcessRunnerServiceMock(CodeSnifferCommandExecutor::EXECUTION_ATTEMPTS, $this->createProcessMock(1, '')), + ); + + // Act + $codeSnifferCommandExecutor->executeCodeSnifferCommand([]); + } + + /** + * @return void + */ + public function testExecuteCodeSnifferCommandShouldFailWhenErrorOutputNotEmpty(): void + { + // Arrange + $exceptionMessage = 'Some error'; + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage($exceptionMessage); + + $codeSnifferCommandExecutor = new CodeSnifferCommandExecutor( + $this->createProcessRunnerServiceMock(CodeSnifferCommandExecutor::EXECUTION_ATTEMPTS, $this->createProcessMock(1, $exceptionMessage)), + ); + + // Act + $codeSnifferCommandExecutor->executeCodeSnifferCommand([]); + } + + /** + * @param int $expectedExecutionCount + * @param \Symfony\Component\Process\Process $process + * + * @return \SprykerSdk\Utils\Infrastructure\Service\ProcessRunnerServiceInterface + */ + protected function createProcessRunnerServiceMock(int $expectedExecutionCount, Process $process): ProcessRunnerServiceInterface + { + $processRunnerService = $this->createMock(ProcessRunnerServiceInterface::class); + $processRunnerService + ->expects($this->exactly($expectedExecutionCount)) + ->method('run') + ->willReturn($process); + + return $processRunnerService; + } + + /** + * @param int $exitCode + * @param string $errorOutput + * + * @return \Symfony\Component\Process\Process + */ + protected function createProcessMock(int $exitCode, string $errorOutput): Process + { + $process = $this->createMock(Process::class); + $process->method('getExitCode')->willReturn($exitCode); + $process->method('getErrorOutput')->willReturn($errorOutput); + + return $process; + } +} diff --git a/tests/SprykerSdkTest/Integrator/Builder/FileNormalizer/PhpCSFixerFileNormalizerTest.php b/tests/SprykerSdkTest/Integrator/Builder/FileNormalizer/PhpCSFixerFileNormalizerTest.php index 5d358087..66d0f008 100644 --- a/tests/SprykerSdkTest/Integrator/Builder/FileNormalizer/PhpCSFixerFileNormalizerTest.php +++ b/tests/SprykerSdkTest/Integrator/Builder/FileNormalizer/PhpCSFixerFileNormalizerTest.php @@ -10,13 +10,10 @@ namespace SprykerSdkTest\Integrator\Builder\FileNormalizer; use PHPUnit\Framework\TestCase; -use RuntimeException; +use SprykerSdk\Integrator\Builder\FileNormalizer\CodeSnifferCommandExecutor; use SprykerSdk\Integrator\Builder\FileNormalizer\PhpCSFixerFileNormalizer; use SprykerSdk\Integrator\Builder\FileStorage\FileStorage; use SprykerSdk\Integrator\IntegratorConfig; -use SprykerSdk\Utils\Infrastructure\Service\ProcessRunnerService; -use SprykerSdk\Utils\Infrastructure\Service\ProcessRunnerServiceInterface; -use Symfony\Component\Process\Process; class PhpCSFixerFileNormalizerTest extends TestCase { @@ -26,36 +23,10 @@ class PhpCSFixerFileNormalizerTest extends TestCase public function testExecuteSuccess(): void { // Arrange - $processExecutorMock = $this->createProcessExecutorMock(0, ''); - $processExecutorMock->expects($this->atLeastOnce())->method('run')->with( - $this->callback(function ($command) { - return strpos($command[0], 'vendor/bin/phpcbf') !== false; - }), - ); - - // Arrange - $configMock = $this->createMock(IntegratorConfig::class); - $normalizer = new PhpCSFixerFileNormalizer($configMock, $processExecutorMock); - - $fileStorage = new FileStorage(); - $fileStorage->addFile('someClass.php'); - - // Act - $normalizer->normalize($fileStorage->flush()); - } - - /** - * @return void - */ - public function testExecuteFailed(): void - { - // Assert - $this->expectException(RuntimeException::class); - + $codesnifferCommandExecutor = $this->createCodeSnifferCommandExecutorMock(); // Arrange - $processExecutorMock = $this->createProcessExecutorMock(2, 'process error'); $configMock = $this->createMock(IntegratorConfig::class); - $normalizer = new PhpCSFixerFileNormalizer($configMock, $processExecutorMock); + $normalizer = new PhpCSFixerFileNormalizer($configMock, $codesnifferCommandExecutor); $fileStorage = new FileStorage(); $fileStorage->addFile('someClass.php'); @@ -70,9 +41,9 @@ public function testExecuteFailed(): void public function testGetErrorMessageShouldReturnErrorMessage(): void { // Arrange - $processExecutorMock = $this->createProcessExecutorMock(2, 'process error'); + $codesnifferCommandExecutor = $this->createMock(CodeSnifferCommandExecutor::class); $configMock = $this->createMock(IntegratorConfig::class); - $normalizer = new PhpCSFixerFileNormalizer($configMock, $processExecutorMock); + $normalizer = new PhpCSFixerFileNormalizer($configMock, $codesnifferCommandExecutor); // Act $errorMessage = $normalizer->getErrorMessage(); @@ -82,32 +53,19 @@ public function testGetErrorMessageShouldReturnErrorMessage(): void } /** - * @param int $exitCode - * @param string $errorOutput - * - * @return \SprykerSdk\Utils\Infrastructure\Service\ProcessRunnerServiceInterface - */ - protected function createProcessExecutorMock(int $exitCode, string $errorOutput = ''): ProcessRunnerServiceInterface - { - $processMock = $this->createProcessMock($exitCode, $errorOutput); - $processExecutorMock = $this->createMock(ProcessRunnerService::class); - $processExecutorMock->method('run')->willReturn($processMock); - - return $processExecutorMock; - } - - /** - * @param int $exitCode - * @param string $errorOutput - * - * @return \Symfony\Component\Process\Process + * @return \SprykerSdk\Integrator\Builder\FileNormalizer\CodeSnifferCommandExecutor */ - protected function createProcessMock(int $exitCode, string $errorOutput = ''): Process + protected function createCodeSnifferCommandExecutorMock(): CodeSnifferCommandExecutor { - $processMock = $this->createMock(Process::class); - $processMock->method('getExitCode')->willReturn($exitCode); - $processMock->method('getErrorOutput')->willReturn($errorOutput); - - return $processMock; + $codeSnifferCommandExecutor = $this->createMock(CodeSnifferCommandExecutor::class); + $codeSnifferCommandExecutor->expects($this->once()) + ->method('executeCodeSnifferCommand') + ->with( + $this->callback(function ($command) { + return strpos($command[0], 'vendor/bin/phpcbf') !== false; + }), + ); + + return $codeSnifferCommandExecutor; } }