123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513 |
- <?php
- declare(strict_types=1);
- /*
- * This file is part of PHP CS Fixer.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- * Dariusz Rumiński <dariusz.ruminski@gmail.com>
- *
- * This source file is subject to the MIT license that is bundled
- * with this source code in the file LICENSE.
- */
- namespace PhpCsFixer\Tests\Console\Command;
- use org\bovigo\vfs\vfsStream;
- use org\bovigo\vfs\vfsStreamDirectory;
- use org\bovigo\vfs\vfsStreamException;
- use org\bovigo\vfs\vfsStreamWrapper;
- use PhpCsFixer\Console\Application;
- use PhpCsFixer\Console\Command\SelfUpdateCommand;
- use PhpCsFixer\Console\SelfUpdate\GithubClientInterface;
- use PhpCsFixer\Console\SelfUpdate\NewVersionChecker;
- use PhpCsFixer\Console\SelfUpdate\NewVersionCheckerInterface;
- use PhpCsFixer\PharCheckerInterface;
- use PhpCsFixer\Tests\TestCase;
- use PhpCsFixer\ToolInfoInterface;
- use Symfony\Component\Console\Command\Command;
- use Symfony\Component\Console\Tester\CommandTester;
- /**
- * @internal
- *
- * @covers \PhpCsFixer\Console\Command\SelfUpdateCommand
- */
- final class SelfUpdateCommandTest extends TestCase
- {
- /**
- * @var null|vfsStreamDirectory
- */
- private $root;
- protected function setUp(): void
- {
- parent::setUp();
- $this->root = vfsStream::setup();
- file_put_contents($this->getToolPath(), 'Current PHP CS Fixer.');
- file_put_contents($this->root->url().'/'.self::getNewMinorReleaseVersion().'.phar', 'New minor version of PHP CS Fixer.');
- file_put_contents($this->root->url().'/'.self::getNewMajorReleaseVersion().'.phar', 'New major version of PHP CS Fixer.');
- }
- protected function tearDown(): void
- {
- parent::tearDown();
- $this->root = null;
- try {
- vfsStreamWrapper::unregister();
- } catch (vfsStreamException $exception) {
- // ignored
- }
- }
- /**
- * @dataProvider provideCommandNameCases
- */
- public function testCommandName(string $name): void
- {
- $command = new SelfUpdateCommand(
- $this->createNewVersionCheckerDouble(),
- $this->createToolInfoDouble(),
- $this->createPharCheckerDouble(),
- );
- $application = new Application();
- $application->add($command);
- self::assertSame($command, $application->find($name));
- }
- /**
- * @return iterable<array{string}>
- */
- public static function provideCommandNameCases(): iterable
- {
- yield ['self-update'];
- yield ['selfupdate'];
- }
- /**
- * @param array<string, bool|string> $input
- *
- * @dataProvider provideExecuteCases
- */
- public function testExecute(
- string $latestVersion,
- ?string $latestMinorVersion,
- array $input,
- bool $decorated,
- string $expectedFileContents,
- string $expectedDisplay
- ): void {
- $versionChecker = $this->createNewVersionCheckerDouble($latestVersion, $latestMinorVersion);
- $command = new SelfUpdateCommand(
- $versionChecker,
- $this->createToolInfoDouble(),
- $this->createPharCheckerDouble(),
- );
- $commandTester = $this->execute($command, $input, $decorated);
- self::assertSame($expectedFileContents, file_get_contents($this->getToolPath()));
- self::assertDisplay($expectedDisplay, $commandTester);
- self::assertSame(0, $commandTester->getStatusCode());
- }
- public static function provideExecuteCases(): iterable
- {
- $currentVersion = Application::VERSION;
- $minorRelease = self::getNewMinorReleaseVersion();
- $majorRelease = self::getNewMajorReleaseVersion();
- $major = self::getNewMajorVersion();
- $currentContents = 'Current PHP CS Fixer.';
- $minorContents = 'New minor version of PHP CS Fixer.';
- $majorContents = 'New major version of PHP CS Fixer.';
- $upToDateDisplay = "\033[32mPHP CS Fixer is already up-to-date.\033[39m\n";
- $newMinorDisplay = "\033[32mPHP CS Fixer updated\033[39m (\033[33m{$currentVersion}\033[39m -> \033[33m{$minorRelease}\033[39m)\n";
- $newMajorDisplay = "\033[32mPHP CS Fixer updated\033[39m (\033[33m{$currentVersion}\033[39m -> \033[33m{$majorRelease}\033[39m)\n";
- $majorInfoNoMinorDisplay = <<<OUTPUT
- \033[32mA new major version of PHP CS Fixer is available\033[39m (\033[33m{$majorRelease}\033[39m)
- \033[32mBefore upgrading please read\033[39m https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/blob/{$majorRelease}/UPGRADE-v{$major}.md
- \033[32mIf you are ready to upgrade run this command with\033[39m \033[33m-f\033[39m
- \033[32mChecking for new minor/patch version...\033[39m
- \033[32mNo minor update for PHP CS Fixer.\033[39m
- OUTPUT;
- $majorInfoNewMinorDisplay = <<<OUTPUT
- \033[32mA new major version of PHP CS Fixer is available\033[39m (\033[33m{$majorRelease}\033[39m)
- \033[32mBefore upgrading please read\033[39m https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/blob/{$majorRelease}/UPGRADE-v{$major}.md
- \033[32mIf you are ready to upgrade run this command with\033[39m \033[33m-f\033[39m
- \033[32mChecking for new minor/patch version...\033[39m
- \033[32mPHP CS Fixer updated\033[39m (\033[33m{$currentVersion}\033[39m -> \033[33m{$minorRelease}\033[39m)
- OUTPUT;
- // no new version available
- yield [Application::VERSION, Application::VERSION, [], true, $currentContents, $upToDateDisplay];
- yield [Application::VERSION, Application::VERSION, [], false, $currentContents, $upToDateDisplay];
- yield [Application::VERSION, Application::VERSION, ['--force' => true], true, $currentContents, $upToDateDisplay];
- yield [Application::VERSION, Application::VERSION, ['-f' => true], false, $currentContents, $upToDateDisplay];
- // new minor version available
- yield [$minorRelease, $minorRelease, [], true, $minorContents, $newMinorDisplay];
- yield [$minorRelease, $minorRelease, ['--force' => true], true, $minorContents, $newMinorDisplay];
- yield [$minorRelease, $minorRelease, ['-f' => true], true, $minorContents, $newMinorDisplay];
- yield [$minorRelease, $minorRelease, [], false, $minorContents, $newMinorDisplay];
- yield [$minorRelease, $minorRelease, ['--force' => true], false, $minorContents, $newMinorDisplay];
- yield [$minorRelease, $minorRelease, ['-f' => true], false, $minorContents, $newMinorDisplay];
- // new major version available
- yield [$majorRelease, Application::VERSION, [], true, $currentContents, $majorInfoNoMinorDisplay];
- yield [$majorRelease, Application::VERSION, [], false, $currentContents, $majorInfoNoMinorDisplay];
- yield [$majorRelease, Application::VERSION, ['--force' => true], true, $majorContents, $newMajorDisplay];
- yield [$majorRelease, Application::VERSION, ['-f' => true], false, $majorContents, $newMajorDisplay];
- // new minor version and new major version available
- yield [$majorRelease, $minorRelease, [], true, $minorContents, $majorInfoNewMinorDisplay];
- yield [$majorRelease, $minorRelease, [], false, $minorContents, $majorInfoNewMinorDisplay];
- yield [$majorRelease, $minorRelease, ['--force' => true], true, $majorContents, $newMajorDisplay];
- yield [$majorRelease, $minorRelease, ['-f' => true], false, $majorContents, $newMajorDisplay];
- // weird/unexpected versions
- yield ['v0.1.0', 'v0.1.0', [], true, $currentContents, $upToDateDisplay];
- yield ['v0.1.0', 'v0.1.0', [], false, $currentContents, $upToDateDisplay];
- yield ['v0.1.0', 'v0.1.0', ['--force' => true], true, $currentContents, $upToDateDisplay];
- yield ['v0.1.0', 'v0.1.0', ['-f' => true], false, $currentContents, $upToDateDisplay];
- yield ['v0.1.0', null, [], true, $currentContents, $upToDateDisplay];
- yield ['v0.1.0', null, [], false, $currentContents, $upToDateDisplay];
- yield ['v0.1.0', null, ['--force' => true], true, $currentContents, $upToDateDisplay];
- yield ['v0.1.0', null, ['-f' => true], false, $currentContents, $upToDateDisplay];
- yield ['v0.1.0', Application::VERSION, [], true, $currentContents, $upToDateDisplay];
- yield ['v0.1.0', Application::VERSION, [], false, $currentContents, $upToDateDisplay];
- yield ['v0.1.0', Application::VERSION, ['--force' => true], true, $currentContents, $upToDateDisplay];
- yield ['v0.1.0', Application::VERSION, ['-f' => true], false, $currentContents, $upToDateDisplay];
- yield [Application::VERSION, 'v0.1.0', [], true, $currentContents, $upToDateDisplay];
- yield [Application::VERSION, 'v0.1.0', [], false, $currentContents, $upToDateDisplay];
- yield [Application::VERSION, 'v0.1.0', ['--force' => true], true, $currentContents, $upToDateDisplay];
- yield [Application::VERSION, 'v0.1.0', ['-f' => true], false, $currentContents, $upToDateDisplay];
- }
- /**
- * @param array<string, bool|string> $input
- *
- * @dataProvider provideExecuteWhenNotAbleToGetLatestVersionsCases
- */
- public function testExecuteWhenNotAbleToGetLatestVersions(
- bool $latestMajorVersionSuccess,
- bool $latestMinorVersionSuccess,
- array $input,
- bool $decorated
- ): void {
- $versionChecker = $this->createNewVersionCheckerDouble(
- self::getNewMajorReleaseVersion(),
- self::getNewMinorReleaseVersion(),
- $latestMajorVersionSuccess,
- $latestMinorVersionSuccess,
- );
- $command = new SelfUpdateCommand(
- $versionChecker,
- $this->createToolInfoDouble(),
- $this->createPharCheckerDouble(),
- );
- $commandTester = $this->execute($command, $input, $decorated);
- self::assertDisplay(
- "\033[37;41mUnable to determine newest version: Foo.\033[39;49m\n",
- $commandTester
- );
- self::assertSame(1, $commandTester->getStatusCode());
- }
- public static function provideExecuteWhenNotAbleToGetLatestVersionsCases(): iterable
- {
- yield [false, false, [], true];
- yield [false, false, ['--force' => true], true];
- yield [false, false, ['-f' => true], true];
- yield [false, false, [], false];
- yield [false, false, ['--force' => true], false];
- yield [false, false, ['-f' => true], false];
- yield [true, false, [], true];
- yield [true, false, ['--force' => true], true];
- yield [true, false, ['-f' => true], true];
- yield [true, false, [], false];
- yield [true, false, ['--force' => true], false];
- yield [true, false, ['-f' => true], false];
- yield [false, true, [], true];
- yield [false, true, ['--force' => true], true];
- yield [false, true, ['-f' => true], true];
- yield [false, true, [], false];
- yield [false, true, ['--force' => true], false];
- yield [false, true, ['-f' => true], false];
- }
- /**
- * @param array<string, bool|string> $input
- *
- * @dataProvider provideExecuteWhenNotInstalledAsPharCases
- */
- public function testExecuteWhenNotInstalledAsPhar(array $input, bool $decorated): void
- {
- $command = new SelfUpdateCommand(
- $this->createNewVersionCheckerDouble(),
- $this->createToolInfoDouble(false),
- $this->createPharCheckerDouble(),
- );
- $commandTester = $this->execute($command, $input, $decorated);
- self::assertDisplay(
- "\033[37;41mSelf-update is available only for PHAR version.\033[39;49m\n",
- $commandTester
- );
- self::assertSame(1, $commandTester->getStatusCode());
- }
- public static function provideExecuteWhenNotInstalledAsPharCases(): iterable
- {
- yield [[], true];
- yield [['--force' => true], true];
- yield [['-f' => true], true];
- yield [[], false];
- yield [['--force' => true], false];
- yield [['-f' => true], false];
- }
- /**
- * @param array<string, bool|string> $input
- */
- private function execute(Command $command, array $input, bool $decorated): CommandTester
- {
- $application = new Application();
- $application->add($command);
- $input = ['command' => $command->getName()] + $input;
- $commandTester = new CommandTester($command);
- $realPath = $_SERVER['argv'][0];
- $_SERVER['argv'][0] = $this->getToolPath();
- $commandTester->execute($input, ['decorated' => $decorated]);
- $_SERVER['argv'][0] = $realPath;
- return $commandTester;
- }
- private static function assertDisplay(string $expectedDisplay, CommandTester $commandTester): void
- {
- if (!$commandTester->getOutput()->isDecorated()) {
- $expectedDisplay = preg_replace("/\033\\[(\\d+;)*\\d+m/", '', $expectedDisplay);
- }
- self::assertSame(
- $expectedDisplay,
- $commandTester->getDisplay(true)
- );
- }
- private function createToolInfoDouble(bool $isInstalledAsPhar = true): ToolInfoInterface
- {
- return new class($this->root, $isInstalledAsPhar) implements ToolInfoInterface {
- private vfsStreamDirectory $directory;
- private bool $isInstalledAsPhar;
- public function __construct(vfsStreamDirectory $directory, bool $isInstalledAsPhar)
- {
- $this->directory = $directory;
- $this->isInstalledAsPhar = $isInstalledAsPhar;
- }
- public function getComposerInstallationDetails(): array
- {
- throw new \LogicException('Not implemented.');
- }
- public function getComposerVersion(): string
- {
- throw new \LogicException('Not implemented.');
- }
- public function getVersion(): string
- {
- throw new \LogicException('Not implemented.');
- }
- public function isInstalledAsPhar(): bool
- {
- return $this->isInstalledAsPhar;
- }
- public function isInstalledByComposer(): bool
- {
- throw new \LogicException('Not implemented.');
- }
- public function isRunInsideDocker(): bool
- {
- return false;
- }
- public function getPharDownloadUri(string $version): string
- {
- return \sprintf('%s/%s.phar', $this->directory->url(), $version);
- }
- };
- }
- private function getToolPath(): string
- {
- return "{$this->root->url()}/php-cs-fixer";
- }
- private static function getCurrentMajorVersion(): int
- {
- return (int) preg_replace('/^v?(\d+).*$/', '$1', Application::VERSION);
- }
- private static function getNewMinorReleaseVersion(): string
- {
- return self::getCurrentMajorVersion().'.999.0';
- }
- private static function getNewMajorVersion(): int
- {
- return self::getCurrentMajorVersion() + 1;
- }
- private static function getNewMajorReleaseVersion(): string
- {
- return self::getNewMajorVersion().'.0.0';
- }
- private function createNewVersionCheckerDouble(
- string $latestVersion = Application::VERSION,
- ?string $latestMinorVersion = Application::VERSION,
- bool $latestMajorVersionSuccess = true,
- bool $latestMinorVersionSuccess = true
- ): NewVersionCheckerInterface {
- return new class($latestVersion, $latestMinorVersion, $latestMajorVersionSuccess, $latestMinorVersionSuccess) implements NewVersionCheckerInterface {
- private string $latestVersion;
- private ?string $latestMinorVersion;
- private bool $latestMajorVersionSuccess;
- private bool $latestMinorVersionSuccess;
- public function __construct(
- string $latestVersion,
- ?string $latestMinorVersion,
- bool $latestMajorVersionSuccess = true,
- bool $latestMinorVersionSuccess = true
- ) {
- $this->latestVersion = $latestVersion;
- $this->latestMinorVersion = $latestMinorVersion;
- $this->latestMajorVersionSuccess = $latestMajorVersionSuccess;
- $this->latestMinorVersionSuccess = $latestMinorVersionSuccess;
- }
- public function getLatestVersion(): string
- {
- if ($this->latestMajorVersionSuccess) {
- return $this->latestVersion;
- }
- throw new \RuntimeException('Foo.');
- }
- public function getLatestVersionOfMajor(int $majorVersion): ?string
- {
- TestCase::assertSame((int) preg_replace('/^v?(\d+).*$/', '$1', Application::VERSION), $majorVersion);
- if ($this->latestMinorVersionSuccess) {
- return $this->latestMinorVersion;
- }
- throw new \RuntimeException('Foo.');
- }
- public function compareVersions(string $versionA, string $versionB): int
- {
- return (new NewVersionChecker(
- new class implements GithubClientInterface {
- public function getTags(): array
- {
- throw new \LogicException('Not implemented.');
- }
- }
- ))->compareVersions($versionA, $versionB);
- }
- };
- }
- private function createPharCheckerDouble(): PharCheckerInterface
- {
- return new class implements PharCheckerInterface {
- public function checkFileValidity(string $filename): ?string
- {
- return null;
- }
- };
- }
- }
|