From 40098a8dbf2e25c086d6a0d5eb15cd81510bc4db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Tue, 17 Oct 2023 00:12:06 +0200 Subject: [PATCH] refactor: Make the ComposerOrchestrator into a non-static class The goal is to increase the re-usability of this class. --- src/Composer/AutoloadDumper.php | 64 +++++ src/Composer/CompilerPsrLogger.php | 71 +++++ src/Composer/ComposerOrchestrator.php | 260 ++++++------------ src/Composer/ComposerProcessFactory.php | 122 ++++++++ src/Composer/UndetectableComposerVersion.php | 65 +++++ src/Console/Command/Compile.php | 36 ++- ...seComposerOrchestratorComposerTestCase.php | 98 +++++++ ...omposerOrchestratorComposer22TestCase.php} | 93 +------ ...omposerOrchestratorComposer23TestCase.php} | 93 +------ ...omposerOrchestratorComposer24TestCase.php} | 91 +----- 10 files changed, 555 insertions(+), 438 deletions(-) create mode 100644 src/Composer/AutoloadDumper.php create mode 100644 src/Composer/CompilerPsrLogger.php create mode 100644 src/Composer/ComposerProcessFactory.php create mode 100644 src/Composer/UndetectableComposerVersion.php create mode 100644 tests/Composer/BaseComposerOrchestratorComposerTestCase.php rename tests/Composer/{ComposerOrchestratorComposer22Test.php => ComposerOrchestratorComposer22TestCase.php} (89%) rename tests/Composer/{ComposerOrchestratorComposer23Test.php => ComposerOrchestratorComposer23TestCase.php} (90%) rename tests/Composer/{ComposerOrchestratorComposer24Test.php => ComposerOrchestratorComposer24TestCase.php} (91%) diff --git a/src/Composer/AutoloadDumper.php b/src/Composer/AutoloadDumper.php new file mode 100644 index 000000000..dc8468914 --- /dev/null +++ b/src/Composer/AutoloadDumper.php @@ -0,0 +1,64 @@ + + * Théo Fidry + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace KevinGH\Box\Composer; + +use Humbug\PhpScoper\Autoload\ScoperAutoloadGenerator; +use Humbug\PhpScoper\Symbol\SymbolsRegistry; +use KevinGH\Box\NotInstantiable; +use function preg_replace; +use function str_replace; +use const PHP_EOL; + +final class AutoloadDumper +{ + use NotInstantiable; + + public static function generateAutoloadStatements( + SymbolsRegistry $symbolsRegistry, + string $autoloadContents, + ): string { + if (0 === $symbolsRegistry->count()) { + return $autoloadContents; + } + + $autoloadContents = str_replace('dump(); + + $scoperStatements = preg_replace( + '/scoper\-autoload\.php \@generated by PhpScoper/', + '@generated by Humbug Box', + $scoperStatements, + ); + + $scoperStatements = preg_replace( + '/(\s*\\$loader \= .*)/', + $autoloadContents, + $scoperStatements, + ); + + return preg_replace( + '/\n{2,}/m', + PHP_EOL.PHP_EOL, + $scoperStatements, + ); + } +} diff --git a/src/Composer/CompilerPsrLogger.php b/src/Composer/CompilerPsrLogger.php new file mode 100644 index 000000000..fdd52fdca --- /dev/null +++ b/src/Composer/CompilerPsrLogger.php @@ -0,0 +1,71 @@ + + * Théo Fidry + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace KevinGH\Box\Composer; + +use KevinGH\Box\Console\Logger\CompilerLogger; +use Psr\Log\AbstractLogger; +use Psr\Log\LogLevel; +use Stringable; +use Symfony\Component\Console\Output\OutputInterface; +use function array_key_exists; + +final class CompilerPsrLogger extends AbstractLogger +{ + public function __construct( + private CompilerLogger $decoratedLogger, + ) { + } + + public function log($level, Stringable|string $message, array $context = []): void + { + $verbosity = self::getVerbosity($level); + $output = self::getOutput($context); + + if (null === $output) { + $this->decoratedLogger->log( + CompilerLogger::CHEVRON_PREFIX, + $message, + $verbosity, + ); + } else { + $this->decoratedLogger->getIO()->writeln( + $output, + $verbosity, + ); + } + } + + private static function getVerbosity(string $level): int + { + return match ($level) { + LogLevel::INFO => OutputInterface::VERBOSITY_VERBOSE, + LogLevel::DEBUG => OutputInterface::VERBOSITY_VERY_VERBOSE, + default => OutputInterface::OUTPUT_NORMAL, + }; + } + + private static function getOutput(array $context): ?string + { + $knownKeys = ['stdout', 'stderr']; + + foreach ($knownKeys as $knownKey) { + if (array_key_exists($knownKey, $context)) { + return $context[$knownKey]; + } + } + + return null; + } +} diff --git a/src/Composer/ComposerOrchestrator.php b/src/Composer/ComposerOrchestrator.php index 7fd0a2544..a4b39b465 100644 --- a/src/Composer/ComposerOrchestrator.php +++ b/src/Composer/ComposerOrchestrator.php @@ -16,21 +16,15 @@ use Composer\Semver\Semver; use Fidry\Console\Input\IO; -use Fidry\FileSystem\FS; -use Humbug\PhpScoper\Autoload\ScoperAutoloadGenerator; +use Fidry\FileSystem\FileSystem; use Humbug\PhpScoper\Symbol\SymbolsRegistry; -use KevinGH\Box\Console\Logger\CompilerLogger; use KevinGH\Box\NotInstantiable; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; use RuntimeException; -use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Process\Exception\ProcessFailedException; -use Symfony\Component\Process\ExecutableFinder; -use Symfony\Component\Process\Process; -use function preg_replace; use function sprintf; -use function str_replace; use function trim; -use const KevinGH\Box\BOX_ALLOW_XDEBUG; use const PHP_EOL; /** @@ -40,168 +34,108 @@ final class ComposerOrchestrator { use NotInstantiable; - private const SUPPORTED_VERSION_CONSTRAINTS = '^2.2.0'; + public const SUPPORTED_VERSION_CONSTRAINTS = '^2.2.0'; - /** - * @throws IncompatibleComposerVersion - */ - public static function checkVersion( - ?string $composerBin = null, - ?IO $io = null, - ): void { - $logger = new CompilerLogger($io ?? IO::createNull()); - - $version = self::getVersion($composerBin, $io); + private string $detectedVersion; - $logger->log( - CompilerLogger::CHEVRON_PREFIX, - sprintf( - '%s (Box requires %s)', - $version, - self::SUPPORTED_VERSION_CONSTRAINTS, - ), - OutputInterface::VERBOSITY_VERBOSE, + public static function create(): self + { + return new self( + ComposerProcessFactory::create(io: IO::createNull()), + new NullLogger(), + new FileSystem(), ); + } - if (!Semver::satisfies($version, self::SUPPORTED_VERSION_CONSTRAINTS)) { - throw IncompatibleComposerVersion::create($version, self::SUPPORTED_VERSION_CONSTRAINTS); - } + public function __construct( + private ComposerProcessFactory $processFactory, + private LoggerInterface $logger, + private FileSystem $fileSystem, + ) { } - public static function getVersion( - ?string $composerBin = null, - ?IO $io = null, - ): string { - $logger = new CompilerLogger($io ?? IO::createNull()); + /** + * @throws UndetectableComposerVersion + */ + public function getVersion(): string + { + if (isset($this->detectedVersion)) { + return $this->detectedVersion; + } - $composerExecutable = $composerBin ?? self::retrieveComposerExecutable(); - $getVersionProcess = new Process([$composerExecutable, '--version', '--no-ansi']); + $getVersionProcess = $this->processFactory->getVersionProcess(); - $logger->log( - CompilerLogger::CHEVRON_PREFIX, - $getVersionProcess->getCommandLine(), - OutputInterface::VERBOSITY_VERBOSE, - ); + $this->logger->info($getVersionProcess->getCommandLine()); - $getVersionProcess->run(null, self::getDefaultEnvVars()); + $getVersionProcess->run(env: $this->processFactory->getDefaultEnvVars()); if (false === $getVersionProcess->isSuccessful()) { - throw new ProcessFailedException($getVersionProcess); + throw UndetectableComposerVersion::forFailedProcess($getVersionProcess); } $output = $getVersionProcess->getOutput(); - if (preg_match('/^Composer version ([^\\s]+)/', $output, $match) > 0) { - return $match[1]; + if (1 !== preg_match('/^Composer version ([^\\s]+)/', $output, $match)) { + throw UndetectableComposerVersion::forOutput( + $getVersionProcess, + $output, + ); } - throw new RuntimeException( + $this->detectedVersion = $match[1]; + + return $this->detectedVersion; + } + + /** + * @throws UndetectableComposerVersion + * @throws IncompatibleComposerVersion + */ + public function checkVersion(): void + { + $version = $this->getVersion(); + + $this->logger->info( sprintf( - 'Could not determine the Composer version from "%s".', - $output, + 'Version detected: %s (Box requires %s)', + $version, + self::SUPPORTED_VERSION_CONSTRAINTS, ), ); + + if (!Semver::satisfies($version, self::SUPPORTED_VERSION_CONSTRAINTS)) { + throw IncompatibleComposerVersion::create($version, self::SUPPORTED_VERSION_CONSTRAINTS); + } } - public static function dumpAutoload( + public function dumpAutoload( SymbolsRegistry $symbolsRegistry, string $prefix, bool $excludeDevFiles, - ?string $composerBin = null, - ?IO $io = null, ): void { - $logger = new CompilerLogger($io ?? IO::createNull()); - - $composerExecutable = $composerBin ?? self::retrieveComposerExecutable(); - - self::dumpAutoloader($composerExecutable, true === $excludeDevFiles, $logger); - - if ('' !== $prefix) { - $autoloadFile = self::retrieveAutoloadFile($composerExecutable, $logger); - - $autoloadContents = self::generateAutoloadStatements( - $symbolsRegistry, - FS::getFileContents($autoloadFile), - ); - - FS::dumpFile($autoloadFile, $autoloadContents); - } - } + $this->dumpAutoloader(true === $excludeDevFiles); - private static function generateAutoloadStatements( - SymbolsRegistry $symbolsRegistry, - string $autoload, - ): string { - if (0 === $symbolsRegistry->count()) { - return $autoload; + if ('' === $prefix) { + return; } - $autoload = str_replace('dump(); + $autoloadFile = $this->retrieveAutoloadFile(); - $scoperStatements = preg_replace( - '/scoper\-autoload\.php \@generated by PhpScoper/', - '@generated by Humbug Box', - $scoperStatements, + $autoloadContents = AutoloadDumper::generateAutoloadStatements( + $symbolsRegistry, + $this->fileSystem->getFileContents($autoloadFile), ); - $scoperStatements = preg_replace( - '/(\s*\\$loader \= .*)/', - $autoload, - $scoperStatements, - ); - - return preg_replace( - '/\n{2,}/m', - PHP_EOL.PHP_EOL, - $scoperStatements, - ); - } - - private static function retrieveComposerExecutable(): string - { - $executableFinder = new ExecutableFinder(); - $executableFinder->addSuffix('.phar'); - - if (null === $composer = $executableFinder->find('composer')) { - throw new RuntimeException('Could not find a Composer executable.'); - } - - return $composer; + $this->fileSystem->dumpFile($autoloadFile, $autoloadContents); } - private static function dumpAutoloader(string $composerExecutable, bool $noDev, CompilerLogger $logger): void + private function dumpAutoloader(bool $noDev): void { - $composerCommand = [$composerExecutable, 'dump-autoload', '--classmap-authoritative']; + $dumpAutoloadProcess = $this->processFactory->getDumpAutoloaderProcess($noDev); - if (true === $noDev) { - $composerCommand[] = '--no-dev'; - } - - if (null !== $verbosity = self::retrieveSubProcessVerbosity($logger->getIO())) { - $composerCommand[] = $verbosity; - } - - if ($logger->getIO()->isDecorated()) { - $composerCommand[] = '--ansi'; - } - - $dumpAutoloadProcess = new Process($composerCommand); - - $logger->log( - CompilerLogger::CHEVRON_PREFIX, - $dumpAutoloadProcess->getCommandLine(), - OutputInterface::VERBOSITY_VERBOSE, - ); + $this->logger->info($dumpAutoloadProcess->getCommandLine()); - $dumpAutoloadProcess->run(null, self::getDefaultEnvVars()); + $dumpAutoloadProcess->run($this->processFactory->getDefaultEnvVars()); if (false === $dumpAutoloadProcess->isSuccessful()) { throw new RuntimeException( @@ -211,32 +145,26 @@ private static function dumpAutoloader(string $composerExecutable, bool $noDev, ); } - if ('' !== $output = $dumpAutoloadProcess->getOutput()) { - $logger->getIO()->writeln($output, OutputInterface::VERBOSITY_VERBOSE); - } + $output = $dumpAutoloadProcess->getOutput(); + $errorOutput = $dumpAutoloadProcess->getErrorOutput(); - if ('' !== $output = $dumpAutoloadProcess->getErrorOutput()) { - $logger->getIO()->writeln($output, OutputInterface::VERBOSITY_VERBOSE); - } + $this->logger->info( + 'STDOUT output:'.PHP_EOL.$output, + ['stdout' => $output], + ); + $this->logger->info( + 'STDERR output:'.PHP_EOL.$errorOutput, + ['stderr' => $errorOutput], + ); } - private static function retrieveAutoloadFile(string $composerExecutable, CompilerLogger $logger): string + private function retrieveAutoloadFile(): string { - $command = [$composerExecutable, 'config', 'vendor-dir']; + $vendorDirProcess = $this->processFactory->getAutoloadFileProcess(); - if ($logger->getIO()->isDecorated()) { - $command[] = '--ansi'; - } + $this->logger->info($vendorDirProcess->getCommandLine()); - $vendorDirProcess = new Process($command); - - $logger->log( - CompilerLogger::CHEVRON_PREFIX, - $vendorDirProcess->getCommandLine(), - OutputInterface::VERBOSITY_VERBOSE, - ); - - $vendorDirProcess->run(null, self::getDefaultEnvVars()); + $vendorDirProcess->run(env: $this->processFactory->getDefaultEnvVars()); if (false === $vendorDirProcess->isSuccessful()) { throw new RuntimeException( @@ -248,28 +176,4 @@ private static function retrieveAutoloadFile(string $composerExecutable, Compile return trim($vendorDirProcess->getOutput()).'/autoload.php'; } - - private static function retrieveSubProcessVerbosity(IO $io): ?string - { - if ($io->isDebug()) { - return '-vvv'; - } - - if ($io->isVeryVerbose()) { - return '-v'; - } - - return null; - } - - private static function getDefaultEnvVars(): array - { - $vars = []; - - if ('1' === (string) getenv(BOX_ALLOW_XDEBUG)) { - $vars['COMPOSER_ALLOW_XDEBUG'] = '1'; - } - - return $vars; - } } diff --git a/src/Composer/ComposerProcessFactory.php b/src/Composer/ComposerProcessFactory.php new file mode 100644 index 000000000..1f0507029 --- /dev/null +++ b/src/Composer/ComposerProcessFactory.php @@ -0,0 +1,122 @@ + + * Théo Fidry + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace KevinGH\Box\Composer; + +use Fidry\Console\Input\IO; +use RuntimeException; +use Symfony\Component\Process\ExecutableFinder; +use Symfony\Component\Process\Process; +use const KevinGH\Box\BOX_ALLOW_XDEBUG; + +/** + * @private + */ +final class ComposerProcessFactory +{ + public static function create( + ?string $composerExecutable = null, + ?IO $io = null, + ): self { + $io ??= IO::createNull(); + + return new self( + $composerExecutable ?? self::retrieveComposerExecutable(), + self::retrieveSubProcessVerbosity($io), + $io->isDecorated(), + ); + } + + public function __construct( + public readonly string $composerExecutable, + private ?string $verbosity, + private bool $ansi, + ) { + } + + public function getVersionProcess(): Process + { + // Never use ANSI support here as we want to parse the raw output. + return new Process([ + $this->composerExecutable, + '--version', + '--no-ansi', + ]); + } + + public function getDumpAutoloaderProcess(bool $noDev): Process + { + $composerCommand = [$this->composerExecutable, 'dump-autoload', '--classmap-authoritative']; + + if (true === $noDev) { + $composerCommand[] = '--no-dev'; + } + + if (null !== $this->verbosity) { + $composerCommand[] = $this->verbosity; + } + + if ($this->ansi) { + $composerCommand[] = '--ansi'; + } + + return new Process($composerCommand); + } + + public function getAutoloadFileProcess(): Process + { + return new Process([ + $this->composerExecutable, + 'config', + 'vendor-dir', + '--no-ansi', + ]); + } + + private static function retrieveSubProcessVerbosity(IO $io): ?string + { + if ($io->isDebug()) { + return '-vvv'; + } + + if ($io->isVeryVerbose()) { + return '-v'; + } + + return null; + } + + public function getDefaultEnvVars(): array + { + $vars = []; + + if ('1' === (string) getenv(BOX_ALLOW_XDEBUG)) { + $vars['COMPOSER_ALLOW_XDEBUG'] = '1'; + } + + return $vars; + } + + private static function retrieveComposerExecutable(): string + { + $executableFinder = new ExecutableFinder(); + $executableFinder->addSuffix('.phar'); + + if (null === $composer = $executableFinder->find('composer')) { + throw new RuntimeException('Could not find a Composer executable.'); + } + + return $composer; + } +} diff --git a/src/Composer/UndetectableComposerVersion.php b/src/Composer/UndetectableComposerVersion.php new file mode 100644 index 000000000..f2c6115b7 --- /dev/null +++ b/src/Composer/UndetectableComposerVersion.php @@ -0,0 +1,65 @@ + + * Théo Fidry + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace KevinGH\Box\Composer; + +use JetBrains\PhpStorm\Pure; +use RuntimeException; +use Symfony\Component\Process\Exception\ProcessFailedException; +use Symfony\Component\Process\Process; +use Throwable; +use function implode; +use function sprintf; +use const PHP_EOL; + +final class UndetectableComposerVersion extends RuntimeException +{ + #[Pure] + public function __construct( + string $message, + public readonly ?Process $process = null, + int $code = 0, + ?Throwable $previous = null, + ) { + parent::__construct($message, $code, $previous); + } + + public static function forFailedProcess(Process $process): self + { + $previous = new ProcessFailedException($process); + + return new self( + sprintf( + 'Could not detect the Composer version: %s', + $previous->getMessage(), + ), + $process, + previous: $previous, + ); + } + + public static function forOutput(Process $process, string $normalizedOutput): self + { + return new self( + implode( + PHP_EOL, + [ + 'Could not determine the Composer version from the following output:', + $normalizedOutput, + ], + ), + $process, + ); + } +} diff --git a/src/Console/Command/Compile.php b/src/Console/Command/Compile.php index 759d52c9b..1fee366d5 100644 --- a/src/Console/Command/Compile.php +++ b/src/Console/Command/Compile.php @@ -21,13 +21,17 @@ use Fidry\Console\Command\Configuration as CommandConfiguration; use Fidry\Console\ExitCode; use Fidry\Console\Input\IO; +use Fidry\FileSystem\FileSystem; use Fidry\FileSystem\FS; use Humbug\PhpScoper\Symbol\SymbolsRegistry; use KevinGH\Box\Amp\FailureCollector; use KevinGH\Box\Box; use KevinGH\Box\Compactor\Compactor; +use KevinGH\Box\Composer\CompilerPsrLogger; use KevinGH\Box\Composer\ComposerConfiguration; use KevinGH\Box\Composer\ComposerOrchestrator; +use KevinGH\Box\Composer\ComposerOrchestrator; +use KevinGH\Box\Composer\ComposerProcessFactory; use KevinGH\Box\Composer\IncompatibleComposerVersion; use KevinGH\Box\Configuration\Configuration; use KevinGH\Box\Console\Logger\CompilerLogger; @@ -242,8 +246,16 @@ private function createPhar( bool $debug, ): Box { $box = Box::create($config->getTmpOutputPath()); + $composerOrchestrator = new ComposerOrchestrator( + ComposerProcessFactory::create( + $config->getComposerBin(), + $io, + ), + new CompilerPsrLogger($logger), + new FileSystem(), + ); - self::checkComposerVersion($config, $logger, $io); + self::checkComposerVersion($composerOrchestrator, $config, $logger, $io); $box->startBuffering(); @@ -262,7 +274,7 @@ private function createPhar( self::registerStub($config, $box, $main, $check, $logger); self::configureMetadata($config, $box, $logger); - self::commit($box, $config, $logger); + self::commit($box, $composerOrchestrator, $config, $logger); self::checkComposerFiles($box, $config, $logger); @@ -323,6 +335,7 @@ private function removeExistingArtifacts(Configuration $config, CompilerLogger $ } private static function checkComposerVersion( + ComposerOrchestrator $composerOrchestrator, Configuration $config, CompilerLogger $logger, IO $io, @@ -342,10 +355,7 @@ private static function checkComposerVersion( ); try { - ComposerOrchestrator::checkVersion( - $config->getComposerBin(), - $io, - ); + $composerOrchestrator->checkVersion(); $logger->log( CompilerLogger::CHEVRON_PREFIX, @@ -643,26 +653,26 @@ private static function configureMetadata(Configuration $config, Box $box, Compi } } - private static function commit(Box $box, Configuration $config, CompilerLogger $logger): void - { + private static function commit( + Box $box, + ComposerOrchestrator $composerOrchestrator, + Configuration $config, + CompilerLogger $logger, + ): void { $message = $config->dumpAutoload() ? 'Dumping the Composer autoloader' : 'Skipping dumping the Composer autoloader'; $logger->log(CompilerLogger::QUESTION_MARK_PREFIX, $message); - $composerBin = $config->getComposerBin(); $excludeDevFiles = $config->excludeDevFiles(); - $io = $logger->getIO(); $box->endBuffering( $config->dumpAutoload() - ? static fn (SymbolsRegistry $symbolsRegistry, string $prefix) => ComposerOrchestrator::dumpAutoload( + ? static fn (SymbolsRegistry $symbolsRegistry, string $prefix) => $composerOrchestrator->dumpAutoload( $symbolsRegistry, $prefix, $excludeDevFiles, - $composerBin, - $io, ) : null, ); diff --git a/tests/Composer/BaseComposerOrchestratorComposerTestCase.php b/tests/Composer/BaseComposerOrchestratorComposerTestCase.php new file mode 100644 index 000000000..750c87a75 --- /dev/null +++ b/tests/Composer/BaseComposerOrchestratorComposerTestCase.php @@ -0,0 +1,98 @@ + + * Théo Fidry + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace KevinGH\Box\Composer; + +use Humbug\PhpScoper\Symbol\SymbolsRegistry; +use KevinGH\Box\Test\FileSystemTestCase; +use PhpParser\Node\Name\FullyQualified; +use Symfony\Component\Finder\Finder; +use function file_exists; +use function iterator_to_array; +use function sprintf; +use function version_compare; + +abstract class BaseComposerOrchestratorComposerTestCase extends FileSystemTestCase +{ + protected const FIXTURES = __DIR__.'/../../fixtures/composer-dump'; + protected const COMPOSER_AUTOLOADER_NAME = 'ComposerAutoloaderInit80c62b20a4a44fb21e8e102ccb92ff05'; + + protected ComposerOrchestrator $composerOrchestrator; + protected string $composerVersion; + protected bool $skip; + + protected function setUp(): void + { + $this->composerOrchestrator = ComposerOrchestrator::create(); + + if (!isset($this->skip)) { + $this->composerVersion = $this->composerOrchestrator->getVersion(); + + $this->skip = version_compare($this->composerVersion, '2.3.0', '>='); + } + + if ($this->skip) { + self::markTestSkipped( + sprintf( + 'Can only be executed with Composer ~2.2.0. Got "%s".', + $this->composerVersion, + ), + ); + } + + parent::setUp(); + } + + /** + * @param array $recordedClasses + * @param array $recordedFunctions + */ + protected static function createSymbolsRegistry(array $recordedClasses = [], array $recordedFunctions = []): SymbolsRegistry + { + $registry = new SymbolsRegistry(); + + foreach ($recordedClasses as [$original, $alias]) { + $registry->recordClass( + new FullyQualified($original), + new FullyQualified($alias), + ); + } + + foreach ($recordedFunctions as [$original, $alias]) { + $registry->recordFunction( + new FullyQualified($original), + new FullyQualified($alias), + ); + } + + return $registry; + } + + /** + * @return string[] + */ + protected function retrievePaths(): array + { + $finder = Finder::create()->files()->in($this->tmp); + + return $this->normalizePaths(iterator_to_array($finder, false)); + } + + protected function skipIfFixturesNotInstalled(string $path): void + { + if (!file_exists($path)) { + self::markTestSkipped('The fixtures were not installed. Run `$ make test_unit` in order to set them all up.'); + } + } +} diff --git a/tests/Composer/ComposerOrchestratorComposer22Test.php b/tests/Composer/ComposerOrchestratorComposer22TestCase.php similarity index 89% rename from tests/Composer/ComposerOrchestratorComposer22Test.php rename to tests/Composer/ComposerOrchestratorComposer22TestCase.php index 7d53ec13d..451dc7f07 100644 --- a/tests/Composer/ComposerOrchestratorComposer22Test.php +++ b/tests/Composer/ComposerOrchestratorComposer22TestCase.php @@ -17,50 +17,19 @@ use Fidry\Console\DisplayNormalizer; use Fidry\FileSystem\FS; use Humbug\PhpScoper\Symbol\SymbolsRegistry; -use KevinGH\Box\Test\FileSystemTestCase; -use PhpParser\Node\Name\FullyQualified; use RuntimeException; -use Symfony\Component\Finder\Finder; -use function file_exists; use function file_get_contents; -use function iterator_to_array; use function preg_replace; -use function sprintf; -use function version_compare; /** + * @covers \KevinGH\Box\Composer\AutoloadDumper * @covers \KevinGH\Box\Composer\ComposerOrchestrator + * @covers \KevinGH\Box\Composer\ComposerProcessFactory * * @internal */ -class ComposerOrchestratorComposer22Test extends FileSystemTestCase +class ComposerOrchestratorComposer22TestCase extends BaseComposerOrchestratorComposerTestCase { - private const FIXTURES = __DIR__.'/../../fixtures/composer-dump'; - private const COMPOSER_AUTOLOADER_NAME = 'ComposerAutoloaderInit80c62b20a4a44fb21e8e102ccb92ff05'; - - private string $composerVersion; - private bool $skip; - - protected function setUp(): void - { - if (!isset($this->skip)) { - $this->composerVersion = ComposerOrchestrator::getVersion(); - - $this->skip = version_compare($this->composerVersion, '2.3.0', '>='); - } - - if ($this->skip) { - self::markTestSkipped( - sprintf( - 'Can only be executed with Composer ~2.2.0. Got "%s".', - $this->composerVersion, - ), - ); - } - - parent::setUp(); - } - /** * @dataProvider composerAutoloadProvider */ @@ -71,7 +40,7 @@ public function test_it_can_dump_the_autoloader_with_an_empty_composer_json( ): void { FS::dumpFile('composer.json', '{}'); - ComposerOrchestrator::dumpAutoload($symbolsRegistry, $prefix, false); + $this->composerOrchestrator->dumpAutoload($symbolsRegistry, $prefix, false); $expectedPaths = [ 'composer.json', @@ -131,7 +100,7 @@ public function test_it_cannot_dump_the_autoloader_with_an_invalid_composer_json FS::dumpFile('composer.json'); try { - ComposerOrchestrator::dumpAutoload($symbolsRegistry, $prefix, false); + $this->composerOrchestrator->dumpAutoload($symbolsRegistry, $prefix, false); self::fail('Expected exception to be thrown.'); } catch (RuntimeException $exception) { @@ -153,7 +122,7 @@ public function test_it_can_dump_the_autoloader_with_a_composer_json_with_a_depe { FS::mirror(self::FIXTURES.'/dir000', $this->tmp); - ComposerOrchestrator::dumpAutoload(new SymbolsRegistry(), '', false); + $this->composerOrchestrator->dumpAutoload(new SymbolsRegistry(), '', false); $expectedPaths = [ 'composer.json', @@ -218,7 +187,7 @@ public function test_it_cannot_dump_the_autoloader_if_the_composer_json_file_is_ string $prefix, ): void { try { - ComposerOrchestrator::dumpAutoload($symbolsRegistry, $prefix, false); + $this->composerOrchestrator->dumpAutoload($symbolsRegistry, $prefix, false); self::fail('Expected exception to be thrown.'); } catch (RuntimeException $exception) { @@ -247,7 +216,7 @@ public function test_it_can_dump_the_autoloader_with_a_composer_json_lock_and_in $this->skipIfFixturesNotInstalled(self::FIXTURES.'/dir001/vendor'); FS::mirror(self::FIXTURES.'/dir001', $this->tmp); - ComposerOrchestrator::dumpAutoload($SymbolsRegistry, $prefix, false); + $this->composerOrchestrator->dumpAutoload($SymbolsRegistry, $prefix, false); // The fact that there is a dependency in the `composer.json` does not change anything to Composer $expectedPaths = [ @@ -331,7 +300,7 @@ public function test_it_can_dump_the_autoloader_with_a_composer_json_lock_and_in PHP; - ComposerOrchestrator::dumpAutoload( + $this->composerOrchestrator->dumpAutoload( new SymbolsRegistry(), '', true, @@ -409,7 +378,7 @@ public function test_it_can_dump_the_autoloader_with_a_composer_json_and_lock_wi $this->skipIfFixturesNotInstalled(self::FIXTURES.'/dir002/vendor'); FS::mirror(self::FIXTURES.'/dir002', $this->tmp); - ComposerOrchestrator::dumpAutoload($symbolsRegistry, $prefix, false); + $this->composerOrchestrator->dumpAutoload($symbolsRegistry, $prefix, false); // The fact that there is a dependency in the `composer.json` does not change anything to Composer $expectedPaths = [ @@ -732,46 +701,4 @@ function humbug_phpscoper_expose_class(string \$exposed, string \$prefixed): voi PHP, ]; } - - /** - * @param array $recordedClasses - * @param array $recordedFunctions - */ - private static function createSymbolsRegistry(array $recordedClasses = [], array $recordedFunctions = []): SymbolsRegistry - { - $registry = new SymbolsRegistry(); - - foreach ($recordedClasses as [$original, $alias]) { - $registry->recordClass( - new FullyQualified($original), - new FullyQualified($alias), - ); - } - - foreach ($recordedFunctions as [$original, $alias]) { - $registry->recordFunction( - new FullyQualified($original), - new FullyQualified($alias), - ); - } - - return $registry; - } - - /** - * @return string[] - */ - private function retrievePaths(): array - { - $finder = Finder::create()->files()->in($this->tmp); - - return $this->normalizePaths(iterator_to_array($finder, false)); - } - - private function skipIfFixturesNotInstalled(string $path): void - { - if (!file_exists($path)) { - self::markTestSkipped('The fixtures were not installed. Run `$ make test_unit` in order to set them all up.'); - } - } } diff --git a/tests/Composer/ComposerOrchestratorComposer23Test.php b/tests/Composer/ComposerOrchestratorComposer23TestCase.php similarity index 90% rename from tests/Composer/ComposerOrchestratorComposer23Test.php rename to tests/Composer/ComposerOrchestratorComposer23TestCase.php index 8b7ea4ea4..bbcb30651 100644 --- a/tests/Composer/ComposerOrchestratorComposer23Test.php +++ b/tests/Composer/ComposerOrchestratorComposer23TestCase.php @@ -17,50 +17,19 @@ use Fidry\Console\DisplayNormalizer; use Fidry\FileSystem\FS; use Humbug\PhpScoper\Symbol\SymbolsRegistry; -use KevinGH\Box\Test\FileSystemTestCase; -use PhpParser\Node\Name\FullyQualified; use RuntimeException; -use Symfony\Component\Finder\Finder; -use function file_exists; use function file_get_contents; -use function iterator_to_array; use function preg_replace; -use function sprintf; -use function version_compare; /** + * @covers \KevinGH\Box\Composer\AutoloadDumper * @covers \KevinGH\Box\Composer\ComposerOrchestrator + * @covers \KevinGH\Box\Composer\ComposerProcessFactory * * @internal */ -class ComposerOrchestratorComposer23Test extends FileSystemTestCase +class ComposerOrchestratorComposer23TestCase extends BaseComposerOrchestratorComposerTestCase { - private const FIXTURES = __DIR__.'/../../fixtures/composer-dump'; - private const COMPOSER_AUTOLOADER_NAME = 'ComposerAutoloaderInit80c62b20a4a44fb21e8e102ccb92ff05'; - - private string $composerVersion; - private bool $skip; - - protected function setUp(): void - { - if (!isset($this->skip)) { - $this->composerVersion = ComposerOrchestrator::getVersion(); - - $this->skip = version_compare($this->composerVersion, '2.3.0', '<') || version_compare($this->composerVersion, '2.4.0', '>='); - } - - if ($this->skip) { - self::markTestSkipped( - sprintf( - 'Can only be executed with Composer ~2.3.0. Got "%s".', - $this->composerVersion, - ), - ); - } - - parent::setUp(); - } - /** * @dataProvider composerAutoloadProvider */ @@ -71,7 +40,7 @@ public function test_it_can_dump_the_autoloader_with_an_empty_composer_json( ): void { FS::dumpFile('composer.json', '{}'); - ComposerOrchestrator::dumpAutoload($symbolsRegistry, $prefix, false); + $this->composerOrchestrator->dumpAutoload($symbolsRegistry, $prefix, false); $expectedPaths = [ 'composer.json', @@ -131,7 +100,7 @@ public function test_it_cannot_dump_the_autoloader_with_an_invalid_composer_json FS::dumpFile('composer.json'); try { - ComposerOrchestrator::dumpAutoload($symbolsRegistry, $prefix, false); + $this->composerOrchestrator->dumpAutoload($symbolsRegistry, $prefix, false); self::fail('Expected exception to be thrown.'); } catch (RuntimeException $exception) { @@ -153,7 +122,7 @@ public function test_it_can_dump_the_autoloader_with_a_composer_json_with_a_depe { FS::mirror(self::FIXTURES.'/dir000', $this->tmp); - ComposerOrchestrator::dumpAutoload(new SymbolsRegistry(), '', false); + $this->composerOrchestrator->dumpAutoload(new SymbolsRegistry(), '', false); $expectedPaths = [ 'composer.json', @@ -223,7 +192,7 @@ public function test_it_cannot_dump_the_autoloader_if_the_composer_json_file_is_ string $prefix, ): void { try { - ComposerOrchestrator::dumpAutoload($symbolsRegistry, $prefix, false); + $this->composerOrchestrator->dumpAutoload($symbolsRegistry, $prefix, false); self::fail('Expected exception to be thrown.'); } catch (RuntimeException $exception) { @@ -252,7 +221,7 @@ public function test_it_can_dump_the_autoloader_with_a_composer_json_lock_and_in $this->skipIfFixturesNotInstalled(self::FIXTURES.'/dir001/vendor'); FS::mirror(self::FIXTURES.'/dir001', $this->tmp); - ComposerOrchestrator::dumpAutoload($SymbolsRegistry, $prefix, false); + $this->composerOrchestrator->dumpAutoload($SymbolsRegistry, $prefix, false); // The fact that there is a dependency in the `composer.json` does not change anything to Composer $expectedPaths = [ @@ -341,7 +310,7 @@ public function test_it_can_dump_the_autoloader_with_a_composer_json_lock_and_in PHP; - ComposerOrchestrator::dumpAutoload( + $this->composerOrchestrator->dumpAutoload( new SymbolsRegistry(), '', true, @@ -419,7 +388,7 @@ public function test_it_can_dump_the_autoloader_with_a_composer_json_and_lock_wi $this->skipIfFixturesNotInstalled(self::FIXTURES.'/dir002/vendor'); FS::mirror(self::FIXTURES.'/dir002', $this->tmp); - ComposerOrchestrator::dumpAutoload($symbolsRegistry, $prefix, false); + $this->composerOrchestrator->dumpAutoload($symbolsRegistry, $prefix, false); // The fact that there is a dependency in the `composer.json` does not change anything to Composer $expectedPaths = [ @@ -772,46 +741,4 @@ function humbug_phpscoper_expose_class(string \$exposed, string \$prefixed): voi PHP, ]; } - - /** - * @param array $recordedClasses - * @param array $recordedFunctions - */ - private static function createSymbolsRegistry(array $recordedClasses = [], array $recordedFunctions = []): SymbolsRegistry - { - $registry = new SymbolsRegistry(); - - foreach ($recordedClasses as [$original, $alias]) { - $registry->recordClass( - new FullyQualified($original), - new FullyQualified($alias), - ); - } - - foreach ($recordedFunctions as [$original, $alias]) { - $registry->recordFunction( - new FullyQualified($original), - new FullyQualified($alias), - ); - } - - return $registry; - } - - /** - * @return string[] - */ - private function retrievePaths(): array - { - $finder = Finder::create()->files()->in($this->tmp); - - return $this->normalizePaths(iterator_to_array($finder, false)); - } - - private function skipIfFixturesNotInstalled(string $path): void - { - if (!file_exists($path)) { - self::markTestSkipped('The fixtures were not installed. Run `$ make test_unit` in order to set them all up.'); - } - } } diff --git a/tests/Composer/ComposerOrchestratorComposer24Test.php b/tests/Composer/ComposerOrchestratorComposer24TestCase.php similarity index 91% rename from tests/Composer/ComposerOrchestratorComposer24Test.php rename to tests/Composer/ComposerOrchestratorComposer24TestCase.php index 7608cf5fb..98cbf8496 100644 --- a/tests/Composer/ComposerOrchestratorComposer24Test.php +++ b/tests/Composer/ComposerOrchestratorComposer24TestCase.php @@ -17,48 +17,19 @@ use Fidry\Console\DisplayNormalizer; use Fidry\FileSystem\FS; use Humbug\PhpScoper\Symbol\SymbolsRegistry; -use KevinGH\Box\Test\FileSystemTestCase; -use PhpParser\Node\Name\FullyQualified; use RuntimeException; -use Symfony\Component\Finder\Finder; -use function file_exists; use function file_get_contents; -use function iterator_to_array; use function preg_replace; /** + * @covers \KevinGH\Box\Composer\AutoloadDumper * @covers \KevinGH\Box\Composer\ComposerOrchestrator + * @covers \KevinGH\Box\Composer\ComposerProcessFactory * * @internal */ -class ComposerOrchestratorComposer24Test extends FileSystemTestCase +class ComposerOrchestratorComposer24TestCase extends BaseComposerOrchestratorComposerTestCase { - private const FIXTURES = __DIR__.'/../../fixtures/composer-dump'; - private const COMPOSER_AUTOLOADER_NAME = 'ComposerAutoloaderInit80c62b20a4a44fb21e8e102ccb92ff05'; - - private string $composerVersion; - private bool $skip; - - protected function setUp(): void - { - if (!isset($this->skip)) { - $this->composerVersion = ComposerOrchestrator::getVersion(); - - $this->skip = version_compare($this->composerVersion, '2.4.0', '<'); - } - - if ($this->skip) { - self::markTestSkipped( - sprintf( - 'Can only be executed with Composer >=2.4.0. Got "%s".', - $this->composerVersion, - ), - ); - } - - parent::setUp(); - } - /** * @dataProvider composerAutoloadProvider */ @@ -69,7 +40,7 @@ public function test_it_can_dump_the_autoloader_with_an_empty_composer_json( ): void { FS::dumpFile('composer.json', '{}'); - ComposerOrchestrator::dumpAutoload($symbolsRegistry, $prefix, false); + $this->composerOrchestrator->dumpAutoload($symbolsRegistry, $prefix, false); $expectedPaths = [ 'composer.json', @@ -129,7 +100,7 @@ public function test_it_cannot_dump_the_autoloader_with_an_invalid_composer_json FS::dumpFile('composer.json'); try { - ComposerOrchestrator::dumpAutoload($symbolsRegistry, $prefix, false); + $this->composerOrchestrator->dumpAutoload($symbolsRegistry, $prefix, false); self::fail('Expected exception to be thrown.'); } catch (RuntimeException $exception) { @@ -151,7 +122,7 @@ public function test_it_can_dump_the_autoloader_with_a_composer_json_with_a_depe { FS::mirror(self::FIXTURES.'/dir000', $this->tmp); - ComposerOrchestrator::dumpAutoload(new SymbolsRegistry(), '', false); + $this->composerOrchestrator->dumpAutoload(new SymbolsRegistry(), '', false); $expectedPaths = [ 'composer.json', @@ -234,7 +205,7 @@ public function test_it_cannot_dump_the_autoloader_if_the_composer_json_file_is_ string $prefix, ): void { try { - ComposerOrchestrator::dumpAutoload($symbolsRegistry, $prefix, false); + $this->composerOrchestrator->dumpAutoload($symbolsRegistry, $prefix, false); self::fail('Expected exception to be thrown.'); } catch (RuntimeException $exception) { @@ -263,7 +234,7 @@ public function test_it_can_dump_the_autoloader_with_a_composer_json_lock_and_in $this->skipIfFixturesNotInstalled(self::FIXTURES.'/dir001/vendor'); FS::mirror(self::FIXTURES.'/dir001', $this->tmp); - ComposerOrchestrator::dumpAutoload($SymbolsRegistry, $prefix, false); + $this->composerOrchestrator->dumpAutoload($SymbolsRegistry, $prefix, false); // The fact that there is a dependency in the `composer.json` does not change anything to Composer $expectedPaths = [ @@ -365,7 +336,7 @@ public function test_it_can_dump_the_autoloader_with_a_composer_json_lock_and_in PHP; - ComposerOrchestrator::dumpAutoload( + $this->composerOrchestrator->dumpAutoload( new SymbolsRegistry(), '', true, @@ -443,7 +414,7 @@ public function test_it_can_dump_the_autoloader_with_a_composer_json_and_lock_wi $this->skipIfFixturesNotInstalled(self::FIXTURES.'/dir002/vendor'); FS::mirror(self::FIXTURES.'/dir002', $this->tmp); - ComposerOrchestrator::dumpAutoload($symbolsRegistry, $prefix, false); + $this->composerOrchestrator->dumpAutoload($symbolsRegistry, $prefix, false); // The fact that there is a dependency in the `composer.json` does not change anything to Composer $expectedPaths = [ @@ -874,46 +845,4 @@ function humbug_phpscoper_expose_class(string \$exposed, string \$prefixed): voi PHP, ]; } - - /** - * @param array $recordedClasses - * @param array $recordedFunctions - */ - private static function createSymbolsRegistry(array $recordedClasses = [], array $recordedFunctions = []): SymbolsRegistry - { - $registry = new SymbolsRegistry(); - - foreach ($recordedClasses as [$original, $alias]) { - $registry->recordClass( - new FullyQualified($original), - new FullyQualified($alias), - ); - } - - foreach ($recordedFunctions as [$original, $alias]) { - $registry->recordFunction( - new FullyQualified($original), - new FullyQualified($alias), - ); - } - - return $registry; - } - - /** - * @return string[] - */ - private function retrievePaths(): array - { - $finder = Finder::create()->files()->in($this->tmp); - - return $this->normalizePaths(iterator_to_array($finder, false)); - } - - private function skipIfFixturesNotInstalled(string $path): void - { - if (!file_exists($path)) { - self::markTestSkipped('The fixtures were not installed. Run `$ make test_unit` in order to set them all up.'); - } - } }