diff --git a/Application.php b/Application.php index d4ec1be09..41548a3e9 100644 --- a/Application.php +++ b/Application.php @@ -87,7 +87,7 @@ class Application implements ResetInterface private string $defaultCommand; private bool $singleCommand = false; private bool $initialized = false; - private SignalRegistry $signalRegistry; + private ?SignalRegistry $signalRegistry = null; private array $signalsToDispatchEvent = []; public function __construct(string $name = 'UNKNOWN', string $version = 'UNKNOWN') diff --git a/Helper/ProgressIndicator.php b/Helper/ProgressIndicator.php index 172036465..b5fa24c46 100644 --- a/Helper/ProgressIndicator.php +++ b/Helper/ProgressIndicator.php @@ -123,8 +123,6 @@ public function advance() /** * Finish the indicator with message. - * - * @param $message */ public function finish(string $message) { diff --git a/Input/InputArgument.php b/Input/InputArgument.php index f0867193e..a130c4122 100644 --- a/Input/InputArgument.php +++ b/Input/InputArgument.php @@ -36,10 +36,10 @@ class InputArgument private string $description; /** - * @param string $name The argument name - * @param int|null $mode The argument mode: self::REQUIRED or self::OPTIONAL - * @param string $description A description text - * @param string|bool|int|float|array|null $default The default value (for self::OPTIONAL mode only) + * @param string $name The argument name + * @param int|null $mode The argument mode: self::REQUIRED or self::OPTIONAL + * @param string $description A description text + * @param string|bool|int|float|array|null $default The default value (for self::OPTIONAL mode only) * @param array|\Closure(CompletionInput,CompletionSuggestions):list $suggestedValues The values used for input completion * * @throws InvalidArgumentException When argument mode is not valid diff --git a/Input/InputOption.php b/Input/InputOption.php index 452c9f7fd..9a7032a03 100644 --- a/Input/InputOption.php +++ b/Input/InputOption.php @@ -58,9 +58,9 @@ class InputOption private string $description; /** - * @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts - * @param int|null $mode The option mode: One of the VALUE_* constants - * @param string|bool|int|float|array|null $default The default value (must be null for self::VALUE_NONE) + * @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts + * @param int|null $mode The option mode: One of the VALUE_* constants + * @param string|bool|int|float|array|null $default The default value (must be null for self::VALUE_NONE) * @param array|\Closure(CompletionInput,CompletionSuggestions):list $suggestedValues The values used for input completion * * @throws InvalidArgumentException If option mode is invalid or incompatible diff --git a/LICENSE b/LICENSE index 008370457..0138f8f07 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2023 Fabien Potencier +Copyright (c) 2004-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Output/ConsoleSectionOutput.php b/Output/ConsoleSectionOutput.php index 7978a922c..c813c811c 100644 --- a/Output/ConsoleSectionOutput.php +++ b/Output/ConsoleSectionOutput.php @@ -115,7 +115,8 @@ public function addContent(string $input, bool $newline = true): int // re-add the line break (that has been removed in the above `explode()` for // - every line that is not the last line // - if $newline is required, also add it to the last line - if ($i < $count || $newline) { + // - if it's not new line, but input ending with `\PHP_EOL` + if ($i < $count || $newline || str_ends_with($input, \PHP_EOL)) { $lineContent .= \PHP_EOL; } @@ -149,6 +150,15 @@ public function addContent(string $input, bool $newline = true): int return $linesAdded; } + /** + * @internal + */ + public function addNewLineOfInputSubmit() + { + $this->content[] = \PHP_EOL; + ++$this->lines; + } + protected function doWrite(string $message, bool $newline) { if (!$this->isDecorated()) { diff --git a/Style/SymfonyStyle.php b/Style/SymfonyStyle.php index 997f86279..8fd6f849f 100644 --- a/Style/SymfonyStyle.php +++ b/Style/SymfonyStyle.php @@ -23,6 +23,7 @@ use Symfony\Component\Console\Helper\TableSeparator; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\ConsoleSectionOutput; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\TrimmedBufferOutput; use Symfony\Component\Console\Question\ChoiceQuestion; @@ -298,6 +299,11 @@ public function askQuestion(Question $question): mixed $answer = $this->questionHelper->ask($this->input, $this, $question); if ($this->input->isInteractive()) { + if ($this->output instanceof ConsoleSectionOutput) { + // add the new line of the `return` to submit the input to ConsoleSectionOutput, because ConsoleSectionOutput is holding all it's lines. + // this is relevant when a `ConsoleSectionOutput::clear` is called. + $this->output->addNewLineOfInputSubmit(); + } $this->newLine(); $this->bufferedOutput->write("\n"); } diff --git a/Tests/ApplicationTest.php b/Tests/ApplicationTest.php index 248d97be8..d0b73389e 100644 --- a/Tests/ApplicationTest.php +++ b/Tests/ApplicationTest.php @@ -66,6 +66,16 @@ protected function tearDown(): void putenv('SHELL_VERBOSITY'); unset($_ENV['SHELL_VERBOSITY']); unset($_SERVER['SHELL_VERBOSITY']); + + if (\function_exists('pcntl_signal')) { + // We reset all signals to their default value to avoid side effects + for ($i = 1; $i <= 15; ++$i) { + if (9 === $i) { + continue; + } + pcntl_signal($i, SIG_DFL); + } + } } public static function setUpBeforeClass(): void @@ -432,7 +442,7 @@ public function testFindWithAmbiguousAbbreviations($abbreviation, $expectedExcep $application->find($abbreviation); } - public function provideAmbiguousAbbreviations() + public static function provideAmbiguousAbbreviations() { return [ ['f', 'Command "f" is not defined.'], @@ -509,15 +519,7 @@ public function testDontRunAlternativeNamespaceName() $application->setAutoExit(false); $tester = new ApplicationTester($application); $tester->run(['command' => 'foos:bar1'], ['decorated' => false]); - $this->assertSame(' - - There are no commands defined in the "foos" namespace. - - Did you mean this? - foo - - -', $tester->getDisplay(true)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application.dont_run_alternative_namespace_name.txt', $tester->getDisplay(true)); } public function testCanRunAlternativeCommandName() @@ -548,7 +550,7 @@ public function testDontRunAlternativeCommandName() $this->assertStringContainsString('Do you want to run "foo" instead? (yes/no) [no]:', $display); } - public function provideInvalidCommandNamesSingle() + public static function provideInvalidCommandNamesSingle() { return [ ['foo3:barr'], @@ -1256,7 +1258,7 @@ public function testAddingAlreadySetDefinitionElementData($def) $application->run($input, $output); } - public function getAddingAlreadySetDefinitionElementData() + public static function getAddingAlreadySetDefinitionElementData() { return [ [new InputArgument('command', InputArgument::REQUIRED)], @@ -1992,15 +1994,38 @@ public function testSetSignalsToDispatchEvent() $dispatcher = new EventDispatcher(); $dispatcher->addSubscriber($subscriber); + // Since there is no signal handler, and by default PHP will stop even + // on SIGUSR1, we need to register a blank handler to avoid the process + // being stopped. + $blankHandlerSignaled = false; + pcntl_signal(\SIGUSR1, function () use (&$blankHandlerSignaled) { + $blankHandlerSignaled = true; + }); + $application = $this->createSignalableApplication($command, $dispatcher); $application->setSignalsToDispatchEvent(\SIGUSR2); $this->assertSame(0, $application->run(new ArrayInput(['signal']))); $this->assertFalse($subscriber->signaled); + $this->assertTrue($blankHandlerSignaled); + + // We reset the blank handler to false to make sure it is called again + $blankHandlerSignaled = false; + + $application = $this->createSignalableApplication($command, $dispatcher); + $application->setSignalsToDispatchEvent(\SIGUSR1); + $this->assertSame(1, $application->run(new ArrayInput(['signal']))); + $this->assertTrue($subscriber->signaled); + $this->assertTrue($blankHandlerSignaled); + + // And now we test without the blank handler + $blankHandlerSignaled = false; + pcntl_signal(\SIGUSR1, SIG_DFL); $application = $this->createSignalableApplication($command, $dispatcher); $application->setSignalsToDispatchEvent(\SIGUSR1); $this->assertSame(1, $application->run(new ArrayInput(['signal']))); $this->assertTrue($subscriber->signaled); + $this->assertFalse($blankHandlerSignaled); } public function testSignalableCommandInterfaceWithoutSignals() diff --git a/Tests/CI/GithubActionReporterTest.php b/Tests/CI/GithubActionReporterTest.php index 6ef190f16..23f7a3bd9 100644 --- a/Tests/CI/GithubActionReporterTest.php +++ b/Tests/CI/GithubActionReporterTest.php @@ -43,7 +43,7 @@ public function testAnnotationsFormat(string $type, string $message, string $fil self::assertSame($expected.\PHP_EOL, $buffer->fetch()); } - public function annotationsFormatProvider(): iterable + public static function annotationsFormatProvider(): iterable { yield 'warning' => ['warning', 'A warning', null, null, null, '::warning::A warning']; yield 'error' => ['error', 'An error', null, null, null, '::error::An error']; diff --git a/Tests/Command/CommandTest.php b/Tests/Command/CommandTest.php index e2172f56a..f85280fed 100644 --- a/Tests/Command/CommandTest.php +++ b/Tests/Command/CommandTest.php @@ -148,7 +148,7 @@ public function testInvalidCommandNames($name) $command->setName($name); } - public function provideInvalidCommandNames() + public static function provideInvalidCommandNames() { return [ [''], @@ -360,7 +360,7 @@ public function testSetCode() $this->assertEquals('interact called'.\PHP_EOL.'from the code...'.\PHP_EOL, $tester->getDisplay()); } - public function getSetCodeBindToClosureTests() + public static function getSetCodeBindToClosureTests() { return [ [true, 'not bound to the command'], diff --git a/Tests/Command/CompleteCommandTest.php b/Tests/Command/CompleteCommandTest.php index fd1039fc9..a8b8f99c7 100644 --- a/Tests/Command/CompleteCommandTest.php +++ b/Tests/Command/CompleteCommandTest.php @@ -81,7 +81,7 @@ public function testInputAndCurrentOptionValidation(array $input, ?string $excep } } - public function provideInputAndCurrentOptionValues() + public static function provideInputAndCurrentOptionValues() { yield [[], 'The "--current" option must be set and it must be an integer']; yield [['--current' => 'a'], 'The "--current" option must be set and it must be an integer']; @@ -100,7 +100,7 @@ public function testCompleteCommandName(array $input, array $suggestions) $this->assertEquals(implode("\n", $suggestions).\PHP_EOL, $this->tester->getDisplay()); } - public function provideCompleteCommandNameInputs() + public static function provideCompleteCommandNameInputs() { yield 'empty' => [['bin/console'], ['help', 'list', 'completion', 'hello', 'ahoy']]; yield 'partial' => [['bin/console', 'he'], ['help', 'list', 'completion', 'hello', 'ahoy']]; @@ -117,7 +117,7 @@ public function testCompleteCommandInputDefinition(array $input, array $suggesti $this->assertEquals(implode("\n", $suggestions).\PHP_EOL, $this->tester->getDisplay()); } - public function provideCompleteCommandInputDefinitionInputs() + public static function provideCompleteCommandInputDefinitionInputs() { yield 'definition' => [['bin/console', 'hello', '-'], ['--help', '--quiet', '--verbose', '--version', '--ansi', '--no-ansi', '--no-interaction']]; yield 'custom' => [['bin/console', 'hello'], ['Fabien', 'Robin', 'Wouter']]; diff --git a/Tests/Command/DumpCompletionCommandTest.php b/Tests/Command/DumpCompletionCommandTest.php index 0c955a0f6..ba23bb331 100644 --- a/Tests/Command/DumpCompletionCommandTest.php +++ b/Tests/Command/DumpCompletionCommandTest.php @@ -28,7 +28,7 @@ public function testComplete(array $input, array $expectedSuggestions) $this->assertSame($expectedSuggestions, $suggestions); } - public function provideCompletionSuggestions() + public static function provideCompletionSuggestions() { yield 'shell' => [ [''], diff --git a/Tests/Command/HelpCommandTest.php b/Tests/Command/HelpCommandTest.php index 0e8a7f4f7..d61c912fc 100644 --- a/Tests/Command/HelpCommandTest.php +++ b/Tests/Command/HelpCommandTest.php @@ -83,7 +83,7 @@ public function testComplete(array $input, array $expectedSuggestions) $this->assertSame($expectedSuggestions, $suggestions); } - public function provideCompletionSuggestions() + public static function provideCompletionSuggestions() { yield 'option --format' => [ ['--format', ''], diff --git a/Tests/Command/ListCommandTest.php b/Tests/Command/ListCommandTest.php index 0f78cf7ee..7ed9d3d5d 100644 --- a/Tests/Command/ListCommandTest.php +++ b/Tests/Command/ListCommandTest.php @@ -127,7 +127,7 @@ public function testComplete(array $input, array $expectedSuggestions) $this->assertSame($expectedSuggestions, $suggestions); } - public function provideCompletionSuggestions() + public static function provideCompletionSuggestions() { yield 'option --format' => [ ['--format', ''], diff --git a/Tests/Completion/CompletionInputTest.php b/Tests/Completion/CompletionInputTest.php index 946e51d20..5b6a8e42d 100644 --- a/Tests/Completion/CompletionInputTest.php +++ b/Tests/Completion/CompletionInputTest.php @@ -39,7 +39,7 @@ public function testBind(CompletionInput $input, string $expectedType, ?string $ $this->assertEquals($expectedValue, $input->getCompletionValue(), 'Unexpected value'); } - public function provideBindData() + public static function provideBindData() { // option names yield 'optname-minimal-input' => [CompletionInput::fromTokens(['bin/console', '-'], 1), CompletionInput::TYPE_OPTION_NAME, null, '-']; @@ -90,7 +90,7 @@ public function testBindWithLastArrayArgument(CompletionInput $input, ?string $e $this->assertEquals($expectedValue, $input->getCompletionValue(), 'Unexpected value'); } - public function provideBindWithLastArrayArgumentData() + public static function provideBindWithLastArrayArgumentData() { yield [CompletionInput::fromTokens(['bin/console'], 1), null]; yield [CompletionInput::fromTokens(['bin/console', 'symfony', 'sensiolabs'], 3), null]; @@ -123,7 +123,7 @@ public function testFromString($inputStr, array $expectedTokens) $this->assertEquals($expectedTokens, $tokensProperty->getValue($input)); } - public function provideFromStringData() + public static function provideFromStringData() { yield ['bin/console cache:clear', ['bin/console', 'cache:clear']]; yield ['bin/console --env prod', ['bin/console', '--env', 'prod']]; diff --git a/Tests/DependencyInjection/AddConsoleCommandPassTest.php b/Tests/DependencyInjection/AddConsoleCommandPassTest.php index ddda0f472..c2fa8c22a 100644 --- a/Tests/DependencyInjection/AddConsoleCommandPassTest.php +++ b/Tests/DependencyInjection/AddConsoleCommandPassTest.php @@ -113,7 +113,7 @@ public function testProcessFallsBackToDefaultName() $this->assertSame(['new-name' => 'with-default-name'], $container->getDefinition('console.command_loader')->getArgument(1)); } - public function visibilityProvider() + public static function visibilityProvider() { return [ [true], diff --git a/Tests/Descriptor/AbstractDescriptorTest.php b/Tests/Descriptor/AbstractDescriptorTestCase.php similarity index 76% rename from Tests/Descriptor/AbstractDescriptorTest.php rename to Tests/Descriptor/AbstractDescriptorTestCase.php index 209526696..1f813048d 100644 --- a/Tests/Descriptor/AbstractDescriptorTest.php +++ b/Tests/Descriptor/AbstractDescriptorTestCase.php @@ -19,7 +19,7 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\BufferedOutput; -abstract class AbstractDescriptorTest extends TestCase +abstract class AbstractDescriptorTestCase extends TestCase { /** @dataProvider getDescribeInputArgumentTestData */ public function testDescribeInputArgument(InputArgument $argument, $expectedDescription) @@ -54,40 +54,40 @@ public function testDescribeApplication(Application $application, $expectedDescr $this->assertDescription($expectedDescription, $application); } - public function getDescribeInputArgumentTestData() + public static function getDescribeInputArgumentTestData() { - return $this->getDescriptionTestData(ObjectsProvider::getInputArguments()); + return static::getDescriptionTestData(ObjectsProvider::getInputArguments()); } - public function getDescribeInputOptionTestData() + public static function getDescribeInputOptionTestData() { - return $this->getDescriptionTestData(ObjectsProvider::getInputOptions()); + return static::getDescriptionTestData(ObjectsProvider::getInputOptions()); } - public function getDescribeInputDefinitionTestData() + public static function getDescribeInputDefinitionTestData() { - return $this->getDescriptionTestData(ObjectsProvider::getInputDefinitions()); + return static::getDescriptionTestData(ObjectsProvider::getInputDefinitions()); } - public function getDescribeCommandTestData() + public static function getDescribeCommandTestData() { - return $this->getDescriptionTestData(ObjectsProvider::getCommands()); + return static::getDescriptionTestData(ObjectsProvider::getCommands()); } - public function getDescribeApplicationTestData() + public static function getDescribeApplicationTestData() { - return $this->getDescriptionTestData(ObjectsProvider::getApplications()); + return static::getDescriptionTestData(ObjectsProvider::getApplications()); } abstract protected function getDescriptor(); - abstract protected function getFormat(); + abstract protected static function getFormat(); - protected function getDescriptionTestData(array $objects) + protected static function getDescriptionTestData(array $objects) { $data = []; foreach ($objects as $name => $object) { - $description = file_get_contents(sprintf('%s/../Fixtures/%s.%s', __DIR__, $name, $this->getFormat())); + $description = file_get_contents(sprintf('%s/../Fixtures/%s.%s', __DIR__, $name, static::getFormat())); $data[] = [$object, $description]; } diff --git a/Tests/Descriptor/ApplicationDescriptionTest.php b/Tests/Descriptor/ApplicationDescriptionTest.php index 5801be1ab..1933c985c 100644 --- a/Tests/Descriptor/ApplicationDescriptionTest.php +++ b/Tests/Descriptor/ApplicationDescriptionTest.php @@ -31,7 +31,7 @@ public function testGetNamespaces(array $expected, array $names) $this->assertSame($expected, array_keys((new ApplicationDescription($application))->getNamespaces())); } - public function getNamespacesProvider() + public static function getNamespacesProvider() { return [ [['_global'], ['foobar']], diff --git a/Tests/Descriptor/JsonDescriptorTest.php b/Tests/Descriptor/JsonDescriptorTest.php index baaa99275..399bd8f23 100644 --- a/Tests/Descriptor/JsonDescriptorTest.php +++ b/Tests/Descriptor/JsonDescriptorTest.php @@ -13,14 +13,14 @@ use Symfony\Component\Console\Descriptor\JsonDescriptor; -class JsonDescriptorTest extends AbstractDescriptorTest +class JsonDescriptorTest extends AbstractDescriptorTestCase { protected function getDescriptor() { return new JsonDescriptor(); } - protected function getFormat() + protected static function getFormat() { return 'json'; } diff --git a/Tests/Descriptor/MarkdownDescriptorTest.php b/Tests/Descriptor/MarkdownDescriptorTest.php index a8f11cb4a..295c7acea 100644 --- a/Tests/Descriptor/MarkdownDescriptorTest.php +++ b/Tests/Descriptor/MarkdownDescriptorTest.php @@ -15,19 +15,19 @@ use Symfony\Component\Console\Tests\Fixtures\DescriptorApplicationMbString; use Symfony\Component\Console\Tests\Fixtures\DescriptorCommandMbString; -class MarkdownDescriptorTest extends AbstractDescriptorTest +class MarkdownDescriptorTest extends AbstractDescriptorTestCase { - public function getDescribeCommandTestData() + public static function getDescribeCommandTestData() { - return $this->getDescriptionTestData(array_merge( + return self::getDescriptionTestData(array_merge( ObjectsProvider::getCommands(), ['command_mbstring' => new DescriptorCommandMbString()] )); } - public function getDescribeApplicationTestData() + public static function getDescribeApplicationTestData() { - return $this->getDescriptionTestData(array_merge( + return self::getDescriptionTestData(array_merge( ObjectsProvider::getApplications(), ['application_mbstring' => new DescriptorApplicationMbString()] )); @@ -38,7 +38,7 @@ protected function getDescriptor() return new MarkdownDescriptor(); } - protected function getFormat() + protected static function getFormat() { return 'md'; } diff --git a/Tests/Descriptor/TextDescriptorTest.php b/Tests/Descriptor/TextDescriptorTest.php index 26bbd907a..c576ac2d7 100644 --- a/Tests/Descriptor/TextDescriptorTest.php +++ b/Tests/Descriptor/TextDescriptorTest.php @@ -16,19 +16,19 @@ use Symfony\Component\Console\Tests\Fixtures\DescriptorApplicationMbString; use Symfony\Component\Console\Tests\Fixtures\DescriptorCommandMbString; -class TextDescriptorTest extends AbstractDescriptorTest +class TextDescriptorTest extends AbstractDescriptorTestCase { - public function getDescribeCommandTestData() + public static function getDescribeCommandTestData() { - return $this->getDescriptionTestData(array_merge( + return self::getDescriptionTestData(array_merge( ObjectsProvider::getCommands(), ['command_mbstring' => new DescriptorCommandMbString()] )); } - public function getDescribeApplicationTestData() + public static function getDescribeApplicationTestData() { - return $this->getDescriptionTestData(array_merge( + return self::getDescriptionTestData(array_merge( ObjectsProvider::getApplications(), ['application_mbstring' => new DescriptorApplicationMbString()] )); @@ -46,7 +46,7 @@ protected function getDescriptor() return new TextDescriptor(); } - protected function getFormat() + protected static function getFormat() { return 'txt'; } diff --git a/Tests/Descriptor/XmlDescriptorTest.php b/Tests/Descriptor/XmlDescriptorTest.php index 59a5d1ed8..98f92991a 100644 --- a/Tests/Descriptor/XmlDescriptorTest.php +++ b/Tests/Descriptor/XmlDescriptorTest.php @@ -13,14 +13,14 @@ use Symfony\Component\Console\Descriptor\XmlDescriptor; -class XmlDescriptorTest extends AbstractDescriptorTest +class XmlDescriptorTest extends AbstractDescriptorTestCase { protected function getDescriptor() { return new XmlDescriptor(); } - protected function getFormat() + protected static function getFormat() { return 'xml'; } diff --git a/Tests/Fixtures/application.dont_run_alternative_namespace_name.txt b/Tests/Fixtures/application.dont_run_alternative_namespace_name.txt new file mode 100644 index 000000000..430edde20 --- /dev/null +++ b/Tests/Fixtures/application.dont_run_alternative_namespace_name.txt @@ -0,0 +1,8 @@ + + + There are no commands defined in the "foos" namespace. + + Did you mean this? + foo + + diff --git a/Tests/Formatter/OutputFormatterTest.php b/Tests/Formatter/OutputFormatterTest.php index 62db3e9e1..9a1efc37c 100644 --- a/Tests/Formatter/OutputFormatterTest.php +++ b/Tests/Formatter/OutputFormatterTest.php @@ -184,7 +184,7 @@ public function testInlineStyleOptions(string $tag, string $expected = null, str } } - public function provideInlineStyleOptionsCases() + public static function provideInlineStyleOptionsCases() { return [ [''], @@ -257,7 +257,7 @@ public function testNotDecoratedFormatter(string $input, string $expectedNonDeco } } - public function provideDecoratedAndNonDecoratedOutput() + public static function provideDecoratedAndNonDecoratedOutput() { return [ ['some error', 'some error', "\033[37;41msome error\033[39;49m"], diff --git a/Tests/Helper/AbstractQuestionHelperTest.php b/Tests/Helper/AbstractQuestionHelperTestCase.php similarity index 93% rename from Tests/Helper/AbstractQuestionHelperTest.php rename to Tests/Helper/AbstractQuestionHelperTestCase.php index 3da121624..66f45c27f 100644 --- a/Tests/Helper/AbstractQuestionHelperTest.php +++ b/Tests/Helper/AbstractQuestionHelperTestCase.php @@ -14,7 +14,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Input\StreamableInputInterface; -abstract class AbstractQuestionHelperTest extends TestCase +abstract class AbstractQuestionHelperTestCase extends TestCase { protected function createStreamableInputInterfaceMock($stream = null, $interactive = true) { diff --git a/Tests/Helper/DumperNativeFallbackTest.php b/Tests/Helper/DumperNativeFallbackTest.php index 906d322b6..1b37e4e93 100644 --- a/Tests/Helper/DumperNativeFallbackTest.php +++ b/Tests/Helper/DumperNativeFallbackTest.php @@ -42,7 +42,7 @@ public function testInvoke($variable, $primitiveString) $this->assertSame($primitiveString, $dumper($variable)); } - public function provideVariables() + public static function provideVariables() { return [ [null, 'null'], diff --git a/Tests/Helper/DumperTest.php b/Tests/Helper/DumperTest.php index e491422b0..0a30c6ec3 100644 --- a/Tests/Helper/DumperTest.php +++ b/Tests/Helper/DumperTest.php @@ -45,7 +45,7 @@ public function testInvoke($variable) $this->assertDumpMatchesFormat($dumper($variable), $variable); } - public function provideVariables() + public static function provideVariables() { return [ [null], diff --git a/Tests/Helper/HelperTest.php b/Tests/Helper/HelperTest.php index b8c891087..9f59aa2ff 100644 --- a/Tests/Helper/HelperTest.php +++ b/Tests/Helper/HelperTest.php @@ -17,7 +17,7 @@ class HelperTest extends TestCase { - public function formatTimeProvider() + public static function formatTimeProvider() { return [ [0, '< 1 sec'], @@ -43,7 +43,7 @@ public function formatTimeProvider() ]; } - public function decoratedTextProvider() + public static function decoratedTextProvider() { return [ ['abc', 'abc'], diff --git a/Tests/Helper/ProcessHelperTest.php b/Tests/Helper/ProcessHelperTest.php index e2880f22f..b4d7b0bbc 100644 --- a/Tests/Helper/ProcessHelperTest.php +++ b/Tests/Helper/ProcessHelperTest.php @@ -49,7 +49,7 @@ public function testPassedCallbackIsExecuted() $this->assertTrue($executed); } - public function provideCommandsAndOutput() + public static function provideCommandsAndOutput() { $successOutputVerbose = <<<'EOT' RUN php -r "echo 42;" diff --git a/Tests/Helper/ProgressBarTest.php b/Tests/Helper/ProgressBarTest.php index b8f6aa5dc..4378ff227 100644 --- a/Tests/Helper/ProgressBarTest.php +++ b/Tests/Helper/ProgressBarTest.php @@ -1017,7 +1017,7 @@ public function testFormatsWithoutMax($format) /** * Provides each defined format. */ - public function provideFormat(): array + public static function provideFormat(): array { return [ ['normal'], diff --git a/Tests/Helper/ProgressIndicatorTest.php b/Tests/Helper/ProgressIndicatorTest.php index cd97d406e..ffb3472ec 100644 --- a/Tests/Helper/ProgressIndicatorTest.php +++ b/Tests/Helper/ProgressIndicatorTest.php @@ -158,7 +158,7 @@ public function testFormats($format) /** * Provides each defined format. */ - public function provideFormat(): array + public static function provideFormat(): array { return [ ['normal'], diff --git a/Tests/Helper/QuestionHelperTest.php b/Tests/Helper/QuestionHelperTest.php index 1eb3eeed7..c634f7afb 100644 --- a/Tests/Helper/QuestionHelperTest.php +++ b/Tests/Helper/QuestionHelperTest.php @@ -30,7 +30,7 @@ /** * @group tty */ -class QuestionHelperTest extends AbstractQuestionHelperTest +class QuestionHelperTest extends AbstractQuestionHelperTestCase { public function testAskChoice() { @@ -340,7 +340,7 @@ public function testAskWithAutocompleteWithExactMatch() $this->assertSame('b', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); } - public function getInputs() + public static function getInputs() { return [ ['$'], // 1 byte character @@ -536,7 +536,7 @@ public function testAskConfirmation($question, $expected, $default = true) $this->assertEquals($expected, $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question), 'confirmation question should '.($expected ? 'pass' : 'cancel')); } - public function getAskConfirmationData() + public static function getAskConfirmationData() { return [ ['', true], @@ -612,7 +612,7 @@ public function testSelectChoiceFromSimpleChoices($providedAnswer, $expectedValu $this->assertSame($expectedValue, $answer); } - public function simpleAnswerProvider() + public static function simpleAnswerProvider() { return [ [0, 'My environment 1'], @@ -647,7 +647,7 @@ public function testSpecialCharacterChoiceFromMultipleChoiceList($providedAnswer $this->assertSame($expectedValue, $answer); } - public function specialCharacterInMultipleChoice() + public static function specialCharacterInMultipleChoice() { return [ ['.', ['.']], @@ -697,7 +697,7 @@ public function testAmbiguousChoiceFromChoicelist() $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream("My environment\n")), $this->createOutputInterface(), $question); } - public function answerProvider() + public static function answerProvider() { return [ ['env_1', 'env_1'], diff --git a/Tests/Helper/SymfonyQuestionHelperTest.php b/Tests/Helper/SymfonyQuestionHelperTest.php index 72c3f879f..c6a5bf88a 100644 --- a/Tests/Helper/SymfonyQuestionHelperTest.php +++ b/Tests/Helper/SymfonyQuestionHelperTest.php @@ -23,7 +23,7 @@ /** * @group tty */ -class SymfonyQuestionHelperTest extends AbstractQuestionHelperTest +class SymfonyQuestionHelperTest extends AbstractQuestionHelperTestCase { public function testAskChoice() { diff --git a/Tests/Helper/TableTest.php b/Tests/Helper/TableTest.php index b7c090817..8efaa72f5 100644 --- a/Tests/Helper/TableTest.php +++ b/Tests/Helper/TableTest.php @@ -88,7 +88,7 @@ public function testRenderAddRowsOneByOne($headers, $rows, $style, $expected, $d $this->assertEquals($expected, $this->getOutputContent($output)); } - public function renderProvider() + public static function renderProvider() { $books = [ ['99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'], @@ -1268,7 +1268,7 @@ public function testSetTitle($headerTitle, $footerTitle, $style, $expected) $this->assertEquals($expected, $this->getOutputContent($output)); } - public function renderSetTitle() + public static function renderSetTitle() { return [ [ @@ -1481,7 +1481,7 @@ public function testBoxedStyleWithColspan() $this->assertSame($expected, $this->getOutputContent($output)); } - public function provideRenderHorizontalTests() + public static function provideRenderHorizontalTests() { $headers = ['foo', 'bar', 'baz']; $rows = [['one', 'two', 'tree'], ['1', '2', '3']]; @@ -1600,7 +1600,7 @@ public function testWithColspanAndMaxWith() $this->assertSame($expected, $this->getOutputContent($output)); } - public function provideRenderVerticalTests(): \Traversable + public static function provideRenderVerticalTests(): \Traversable { $books = [ ['99921-58-10-7', 'Divine Comedy', 'Dante Alighieri', '9.95'], diff --git a/Tests/Input/ArgvInputTest.php b/Tests/Input/ArgvInputTest.php index fbbe85e3a..920dc492c 100644 --- a/Tests/Input/ArgvInputTest.php +++ b/Tests/Input/ArgvInputTest.php @@ -60,7 +60,7 @@ public function testParseOptionsNegatable($input, $options, $expectedOptions, $m $this->assertEquals($expectedOptions, $input->getOptions(), $message); } - public function provideOptions() + public static function provideOptions() { return [ [ @@ -186,7 +186,7 @@ public function provideOptions() ]; } - public function provideNegatableOptions() + public static function provideNegatableOptions() { return [ [ @@ -258,7 +258,7 @@ public function testInvalidInputNegatable($argv, $definition, $expectedException $input->bind($definition); } - public function provideInvalidInput() + public static function provideInvalidInput() { return [ [ @@ -329,7 +329,7 @@ public function provideInvalidInput() ]; } - public function provideInvalidNegatableInput() + public static function provideInvalidNegatableInput() { return [ [ @@ -515,7 +515,7 @@ public function testGetParameterOptionEqualSign($argv, $key, $default, $onlyPara $this->assertEquals($expected, $input->getParameterOption($key, $default, $onlyParams), '->getParameterOption() returns the expected value'); } - public function provideGetParameterOptionValues() + public static function provideGetParameterOptionValues() { return [ [['app/console', 'foo:bar'], '-e', 'default', false, 'default'], diff --git a/Tests/Input/ArrayInputTest.php b/Tests/Input/ArrayInputTest.php index 5777c44b7..733322490 100644 --- a/Tests/Input/ArrayInputTest.php +++ b/Tests/Input/ArrayInputTest.php @@ -74,7 +74,7 @@ public function testParseOptions($input, $options, $expectedOptions, $message) $this->assertEquals($expectedOptions, $input->getOptions(), $message); } - public function provideOptions() + public static function provideOptions() { return [ [ @@ -133,7 +133,7 @@ public function testParseInvalidInput($parameters, $definition, $expectedExcepti new ArrayInput($parameters, $definition); } - public function provideInvalidInput() + public static function provideInvalidInput() { return [ [ diff --git a/Tests/Input/InputDefinitionTest.php b/Tests/Input/InputDefinitionTest.php index 11c6ae165..39157af8f 100644 --- a/Tests/Input/InputDefinitionTest.php +++ b/Tests/Input/InputDefinitionTest.php @@ -367,7 +367,7 @@ public function testGetSynopsis(InputDefinition $definition, $expectedSynopsis, $this->assertEquals($expectedSynopsis, $definition->getSynopsis(), $message ? '->getSynopsis() '.$message : ''); } - public function getGetSynopsisData() + public static function getGetSynopsisData() { return [ [new InputDefinition([new InputOption('foo')]), '[--foo]', 'puts optional options in square brackets'], diff --git a/Tests/Input/StringInputTest.php b/Tests/Input/StringInputTest.php index 5257cc258..338c1428a 100644 --- a/Tests/Input/StringInputTest.php +++ b/Tests/Input/StringInputTest.php @@ -42,7 +42,7 @@ public function testInputOptionWithGivenString() $this->assertEquals('bar', $input->getOption('foo')); } - public function getTokenizeData() + public static function getTokenizeData() { return [ ['', [], '->tokenize() parses an empty string'], diff --git a/Tests/Logger/ConsoleLoggerTest.php b/Tests/Logger/ConsoleLoggerTest.php index d0dc85ee3..6baa57bd5 100644 --- a/Tests/Logger/ConsoleLoggerTest.php +++ b/Tests/Logger/ConsoleLoggerTest.php @@ -69,7 +69,7 @@ public function testOutputMapping($logLevel, $outputVerbosity, $isOutput, $addVe $this->assertEquals($isOutput ? "[$logLevel] foo bar".\PHP_EOL : '', $logs); } - public function provideOutputMappingParams() + public static function provideOutputMappingParams() { $quietMap = [LogLevel::EMERGENCY => OutputInterface::VERBOSITY_QUIET]; @@ -123,7 +123,7 @@ public function testLogsAtAllLevels($level, $message) $this->assertEquals($expected, $this->getLogs()); } - public function provideLevelsAndMessages() + public static function provideLevelsAndMessages() { return [ LogLevel::EMERGENCY => [LogLevel::EMERGENCY, 'message of level emergency with context: {user}'], diff --git a/Tests/Output/AnsiColorModeTest.php b/Tests/Output/AnsiColorModeTest.php index a39646440..b9c5c039a 100644 --- a/Tests/Output/AnsiColorModeTest.php +++ b/Tests/Output/AnsiColorModeTest.php @@ -33,7 +33,7 @@ public function testColorsConversionToAnsi8(string $corlorHex, array $expected) $this->assertSame('8;5;'.$expected[AnsiColorMode::Ansi8->name], AnsiColorMode::Ansi8->convertFromHexToAnsiColorCode($corlorHex)); } - public function provideColorsConversion(): \Generator + public static function provideColorsConversion(): \Generator { yield ['#606702', [ AnsiColorMode::Ansi8->name => 100, diff --git a/Tests/Output/OutputTest.php b/Tests/Output/OutputTest.php index 330064973..f337c4ddd 100644 --- a/Tests/Output/OutputTest.php +++ b/Tests/Output/OutputTest.php @@ -104,7 +104,7 @@ public function testWriteRawMessage($message, $type, $expectedOutput) $this->assertEquals($expectedOutput, $output->output); } - public function provideWriteArguments() + public static function provideWriteArguments() { return [ ['foo', Output::OUTPUT_RAW, "foo\n"], @@ -161,7 +161,7 @@ public function testWriteWithVerbosityOption($verbosity, $expected, $msg) $this->assertEquals($expected, $output->output, $msg); } - public function verbosityProvider() + public static function verbosityProvider() { return [ [Output::VERBOSITY_QUIET, '2', '->write() in QUIET mode only outputs when an explicit QUIET verbosity is passed'], diff --git a/Tests/Question/ChoiceQuestionTest.php b/Tests/Question/ChoiceQuestionTest.php index 8de26419f..3738cb7ac 100644 --- a/Tests/Question/ChoiceQuestionTest.php +++ b/Tests/Question/ChoiceQuestionTest.php @@ -39,7 +39,7 @@ public function testSelectUseCases($multiSelect, $answers, $expected, $message, } } - public function selectUseCases() + public static function selectUseCases() { return [ [ @@ -119,7 +119,7 @@ public function testSelectAssociativeChoices($providedAnswer, $expectedValue) $this->assertSame($expectedValue, $question->getValidator()($providedAnswer)); } - public function selectAssociativeChoicesProvider() + public static function selectAssociativeChoicesProvider() { return [ 'select "0" choice by key' => ['0', '0'], diff --git a/Tests/Question/ConfirmationQuestionTest.php b/Tests/Question/ConfirmationQuestionTest.php index 83899772a..44f4c870b 100644 --- a/Tests/Question/ConfirmationQuestionTest.php +++ b/Tests/Question/ConfirmationQuestionTest.php @@ -30,7 +30,7 @@ public function testDefaultRegexUsecases($default, $answers, $expected, $message } } - public function normalizerUsecases() + public static function normalizerUsecases() { return [ [ diff --git a/Tests/Question/QuestionTest.php b/Tests/Question/QuestionTest.php index 55e9d58d4..e6b6fbee0 100644 --- a/Tests/Question/QuestionTest.php +++ b/Tests/Question/QuestionTest.php @@ -24,7 +24,7 @@ protected function setUp(): void $this->question = new Question('Test question'); } - public function providerTrueFalse() + public static function providerTrueFalse() { return [[true], [false]]; } @@ -104,7 +104,7 @@ public function testIsHiddenFallbackDefault() self::assertTrue($this->question->isHiddenFallback()); } - public function providerGetSetAutocompleterValues() + public static function providerGetSetAutocompleterValues() { return [ 'array' => [ @@ -135,7 +135,7 @@ public function testGetSetAutocompleterValues($values, $expectValues) ); } - public function providerSetAutocompleterValuesInvalid() + public static function providerSetAutocompleterValuesInvalid() { return [ ['Potato'], @@ -229,7 +229,7 @@ function (string $input): array { return []; } $this->assertNull($exception); } - public function providerGetSetValidator() + public static function providerGetSetValidator() { return [ [function ($input) { return $input; }], @@ -251,7 +251,7 @@ public function testGetValidatorDefault() self::assertNull($this->question->getValidator()); } - public function providerGetSetMaxAttempts() + public static function providerGetSetMaxAttempts() { return [[1], [5], [null]]; } @@ -265,7 +265,7 @@ public function testGetSetMaxAttempts($attempts) self::assertSame($attempts, $this->question->getMaxAttempts()); } - public function providerSetMaxAttemptsInvalid() + public static function providerSetMaxAttemptsInvalid() { return [[0], [-1]]; } diff --git a/Tests/Style/SymfonyStyleTest.php b/Tests/Style/SymfonyStyleTest.php index 74c240340..f053b60f8 100644 --- a/Tests/Style/SymfonyStyleTest.php +++ b/Tests/Style/SymfonyStyleTest.php @@ -16,11 +16,13 @@ use Symfony\Component\Console\Exception\RuntimeException; use Symfony\Component\Console\Formatter\OutputFormatter; use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\Input; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\ConsoleOutputInterface; use Symfony\Component\Console\Output\ConsoleSectionOutput; use Symfony\Component\Console\Output\NullOutput; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\StreamOutput; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Console\Tester\CommandTester; @@ -69,14 +71,14 @@ public function testInteractiveOutputs($inputCommandFilepath, $outputFilepath) $this->assertStringEqualsFile($outputFilepath, $this->tester->getDisplay(true)); } - public function inputInteractiveCommandToOutputFilesProvider() + public static function inputInteractiveCommandToOutputFilesProvider() { $baseDir = __DIR__.'/../Fixtures/Style/SymfonyStyle'; return array_map(null, glob($baseDir.'/command/interactive_command_*.php'), glob($baseDir.'/output/interactive_output_*.txt')); } - public function inputCommandToOutputFilesProvider() + public static function inputCommandToOutputFilesProvider() { $baseDir = __DIR__.'/../Fixtures/Style/SymfonyStyle'; @@ -151,7 +153,7 @@ public function testCreateTableWithoutConsoleOutput() $style = new SymfonyStyle($input, $output); $this->expectException(RuntimeException::class); - $this->expectDeprecationMessage('Output should be an instance of "Symfony\Component\Console\Output\ConsoleSectionOutput"'); + $this->expectExceptionMessage('Output should be an instance of "Symfony\Component\Console\Output\ConsoleSectionOutput"'); $style->createTable()->appendRow(['row']); } @@ -181,4 +183,44 @@ public function testMemoryConsumption() $this->assertSame(0, memory_get_usage() - $start); } + + public function testAskAndClearExpectFullSectionCleared() + { + $answer = 'Answer'; + $inputStream = fopen('php://memory', 'r+'); + fwrite($inputStream, $answer.\PHP_EOL); + rewind($inputStream); + $input = $this->createMock(Input::class); + $sections = []; + $output = new ConsoleSectionOutput(fopen('php://memory', 'r+', false), $sections, StreamOutput::VERBOSITY_NORMAL, true, new OutputFormatter()); + $input + ->method('isInteractive') + ->willReturn(true); + $input + ->method('getStream') + ->willReturn($inputStream); + + $style = new SymfonyStyle($input, $output); + + $style->writeln('start'); + $style->write('foo'); + $style->writeln(' and bar'); + $givenAnswer = $style->ask('Dummy question?'); + $style->write('foo2'.\PHP_EOL); + $output->write('bar2'); + $output->clear(); + + rewind($output->getStream()); + $this->assertEquals($answer, $givenAnswer); + $this->assertEquals( + 'start'.\PHP_EOL. // write start + 'foo'.\PHP_EOL. // write foo + "\x1b[1A\x1b[0Jfoo and bar".\PHP_EOL. // complete line + \PHP_EOL.\PHP_EOL." \033[32mDummy question?\033[39m:".\PHP_EOL.' > '.\PHP_EOL.\PHP_EOL.\PHP_EOL. // question + 'foo2'.\PHP_EOL.\PHP_EOL. // write foo2 + 'bar2'.\PHP_EOL. // write bar + "\033[12A\033[0J", // clear 12 lines (11 output lines and one from the answer input return) + stream_get_contents($output->getStream()) + ); + } } diff --git a/Tests/TerminalTest.php b/Tests/TerminalTest.php index 5565ff51c..4a9b7a74a 100644 --- a/Tests/TerminalTest.php +++ b/Tests/TerminalTest.php @@ -115,7 +115,7 @@ public function testGetColorMode(?string $testColorTerm, ?string $testTerm, Ansi } } - public function provideTerminalColorEnv(): \Generator + public static function provideTerminalColorEnv(): \Generator { yield ['truecolor', null, AnsiColorMode::Ansi24]; yield ['TRUECOLOR', null, AnsiColorMode::Ansi24]; diff --git a/Tests/Tester/Constraint/CommandIsSuccessfulTest.php b/Tests/Tester/Constraint/CommandIsSuccessfulTest.php index 2de8ac345..7a2b4c719 100644 --- a/Tests/Tester/Constraint/CommandIsSuccessfulTest.php +++ b/Tests/Tester/Constraint/CommandIsSuccessfulTest.php @@ -47,7 +47,7 @@ public function testUnsuccessfulCommand(string $expectedException, int $exitCode $this->fail(); } - public function providesUnsuccessful(): iterable + public static function providesUnsuccessful(): iterable { yield 'Failed' => ['Command failed.', Command::FAILURE]; yield 'Invalid' => ['Command was invalid.', Command::INVALID];