diff --git a/src/ComposerRequireChecker/Cli/CheckCommand.php b/src/ComposerRequireChecker/Cli/CheckCommand.php index 4c47aa32..7eee6d27 100644 --- a/src/ComposerRequireChecker/Cli/CheckCommand.php +++ b/src/ComposerRequireChecker/Cli/CheckCommand.php @@ -37,10 +37,11 @@ use function array_diff; use function array_map; use function array_merge; +use function assert; use function count; use function dirname; -use function file_put_contents; use function gettype; +use function in_array; use function is_string; use function realpath; use function sprintf; @@ -81,6 +82,22 @@ protected function configure(): void ); } + protected function initialize(InputInterface $input, OutputInterface $output): void + { + if ($input->getOption('output') === null) { + return; + } + + $optionValue = $input->getOption('output'); + assert(is_string($optionValue)); + + if (! in_array($optionValue, ['text', 'json'])) { + throw new InvalidArgumentException( + 'Option "output" must be either of value "json", "text" or omitted altogether' + ); + } + } + protected function execute(InputInterface $input, OutputInterface $output): int { if ($input->getOption('output') !== null) { @@ -161,29 +178,29 @@ protected function execute(InputInterface $input, OutputInterface $output): int $options->getSymbolWhitelist() ); - if (! $unknownSymbols) { - $output->writeln('There were no unknown symbols found.'); - - return 0; - } - switch ($input->getOption('output')) { case 'json': $application = $this->getApplication(); $resultsWriter = new CliJson( - static function (string $string): void { - file_put_contents('php://stdout', $string); + static function (string $string) use ($output): void { + $output->write($string, false, OutputInterface::VERBOSITY_QUIET | OutputInterface::OUTPUT_RAW); }, $application !== null ? $application->getVersion() : 'Unknown version', static fn () => new DateTimeImmutable() ); break; case 'text': + $resultsWriter = new CliText( + $output, + static function (string $string) use ($output): void { + $output->write($string, false, OutputInterface::VERBOSITY_QUIET | OutputInterface::OUTPUT_RAW); + } + ); + break; default: $resultsWriter = new CliText($output); } - $output->writeln('The following ' . count($unknownSymbols) . ' unknown symbols were found:'); $guesser = new DependencyGuesser($options); $resultsWriter->write( array_map( diff --git a/src/ComposerRequireChecker/Cli/ResultsWriter/CliText.php b/src/ComposerRequireChecker/Cli/ResultsWriter/CliText.php index 27e1dc57..ede7c791 100644 --- a/src/ComposerRequireChecker/Cli/ResultsWriter/CliText.php +++ b/src/ComposerRequireChecker/Cli/ResultsWriter/CliText.php @@ -5,6 +5,7 @@ namespace ComposerRequireChecker\Cli\ResultsWriter; use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Output\BufferedOutput; use Symfony\Component\Console\Output\OutputInterface; use function count; @@ -13,10 +14,19 @@ final class CliText implements ResultsWriter { private OutputInterface $output; + /** @var callable */ + private $writeCallable; - public function __construct(OutputInterface $output) + public function __construct(OutputInterface $output, ?callable $write = null) { $this->output = $output; + if ($write === null) { + $write = static function (string $string) use ($output): void { + $output->write($string); + }; + } + + $this->writeCallable = $write; } /** @@ -31,12 +41,17 @@ public function write(array $unknownSymbols): void } $this->output->writeln('The following ' . count($unknownSymbols) . ' unknown symbols were found:'); - $table = new Table($this->output); + + $tableOutput = new BufferedOutput(); + $table = new Table($tableOutput); $table->setHeaders(['Unknown Symbol', 'Guessed Dependency']); foreach ($unknownSymbols as $unknownSymbol => $guessedDependencies) { $table->addRow([$unknownSymbol, implode("\n", $guessedDependencies)]); } $table->render(); + + $write = $this->writeCallable; + $write($tableOutput->fetch()); } } diff --git a/test/ComposerRequireCheckerTest/Cli/CheckCommandTest.php b/test/ComposerRequireCheckerTest/Cli/CheckCommandTest.php index b4ae7443..15be1368 100644 --- a/test/ComposerRequireCheckerTest/Cli/CheckCommandTest.php +++ b/test/ComposerRequireCheckerTest/Cli/CheckCommandTest.php @@ -14,12 +14,12 @@ use Symfony\Component\Console\Tester\CommandTester; use function dirname; -use function file_get_contents; use function file_put_contents; use function json_decode; use function unlink; use function version_compare; +use const JSON_THROW_ON_ERROR; use const PHP_VERSION; final class CheckCommandTest extends TestCase @@ -77,22 +77,29 @@ public function testUnknownSymbolsFound(): void $this->assertStringContainsString('libxml_clear_errors', $display); } - public function testUnknownSymbolsFoundJsonReport(): void + public function testInvalidOutputOptionValue(): void { - $vfsRoot = vfsStream::setup(); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Option "output" must be either of value "json", "text" or omitted altogether'); $this->commandTester->execute([ 'composer-json' => dirname(__DIR__, 2) . '/fixtures/unknownSymbols/composer.json', - '--report-json' => 'vfs://root/path/report.json', + '--output' => '__invalid__', + ]); + } + + public function testUnknownSymbolsFoundJsonReport(): void + { + $this->commandTester->execute([ + 'composer-json' => dirname(__DIR__, 2) . '/fixtures/unknownSymbols/composer.json', + '--output' => 'json', ]); $this->assertSame(Command::FAILURE, $this->commandTester->getStatusCode()); $display = $this->commandTester->getDisplay(); - $this->assertStringContainsString('Doctrine\Common\Collections\ArrayCollection', $display); - - /** @var array{'unknown-symbols': array} $actual */ - $actual = json_decode(file_get_contents('vfs://root/path/report.json'), true); + /** @var array{'unknown-symbols': array>} $actual */ + $actual = json_decode($display, true, JSON_THROW_ON_ERROR); $this->assertSame( [ @@ -107,6 +114,25 @@ public function testUnknownSymbolsFoundJsonReport(): void ); } + public function testUnknownSymbolsFoundTextReport(): void + { + $this->commandTester->execute([ + 'composer-json' => dirname(__DIR__, 2) . '/fixtures/unknownSymbols/composer.json', + '--output' => 'text', + ]); + + $this->assertSame(Command::FAILURE, $this->commandTester->getStatusCode()); + $display = $this->commandTester->getDisplay(); + + $this->assertStringNotContainsString('The following 6 unknown symbols were found:', $display); + $this->assertStringContainsString('Doctrine\Common\Collections\ArrayCollection', $display); + $this->assertStringContainsString('Example\Library\Dependency', $display); + $this->assertStringContainsString('FILTER_VALIDATE_URL', $display); + $this->assertStringContainsString('filter_var', $display); + $this->assertStringContainsString('Foo\Bar\Baz', $display); + $this->assertStringContainsString('libxml_clear_errors', $display); + } + public function testSelfCheckShowsNoErrors(): void { $this->commandTester->execute([ diff --git a/test/ComposerRequireCheckerTest/Cli/ResultsWriter/CliTextTest.php b/test/ComposerRequireCheckerTest/Cli/ResultsWriter/CliTextTest.php index e564e62f..1399c480 100644 --- a/test/ComposerRequireCheckerTest/Cli/ResultsWriter/CliTextTest.php +++ b/test/ComposerRequireCheckerTest/Cli/ResultsWriter/CliTextTest.php @@ -7,6 +7,7 @@ use ComposerRequireChecker\Cli\ResultsWriter\CliText; use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Output\BufferedOutput; +use Symfony\Component\Console\Output\OutputInterface; use const PHP_EOL; @@ -47,4 +48,43 @@ public function testWriteReportWithUnknownSymbols(): void self::assertStringContainsString('| ext-dummy', $buffer); self::assertStringContainsString('| ext-other', $buffer); } + + public function testWriteReportQuiet(): void + { + $this->output->setVerbosity(OutputInterface::VERBOSITY_QUIET); + + $this->writer->write([ + 'Foo' => [], + 'opcache_get_status' => ['ext-opcache'], + 'dummy' => ['ext-dummy', 'ext-other'], + ]); + + $buffer = $this->output->fetch(); + self::assertSame('', $buffer); + } + + public function testWriteReportQuietWithWriteCallable(): void + { + $output = ''; + $write = static function (string $string) use (&$output): void { + $output .= $string; + }; + + $writer = new CliText($this->output, $write); + $writer->write([ + 'Foo' => [], + 'opcache_get_status' => ['ext-opcache'], + 'dummy' => ['ext-dummy', 'ext-other'], + ]); + + $buffer = $this->output->fetch(); + self::assertStringContainsString('The following 3 unknown symbols were found:', $buffer); + self::assertStringNotContainsString('Foo', $buffer); + self::assertStringContainsString('Foo', $output); + self::assertStringContainsString('| opcache_get_status', $output); + self::assertStringContainsString('| ext-opcache', $output); + self::assertStringContainsString('| dummy', $output); + self::assertStringContainsString('| ext-dummy', $output); + self::assertStringContainsString('| ext-other', $output); + } }