From a36d9bd25b4fd12b49b147ff04188e483ae69698 Mon Sep 17 00:00:00 2001 From: Brad <28307684+mad-briller@users.noreply.github.com> Date: Tue, 11 Oct 2022 17:28:32 +0100 Subject: [PATCH 1/2] Added junit formatter. --- src/Command/AssertBackwardsCompatible.php | 7 +- src/Formatter/JunitFormatter.php | 71 +++++++++++++ test/unit/Formatter/JunitFormatterTest.php | 90 ++++++++++++++++ test/unit/Formatter/junit.xsd | 118 +++++++++++++++++++++ 4 files changed, 284 insertions(+), 2 deletions(-) create mode 100644 src/Formatter/JunitFormatter.php create mode 100644 test/unit/Formatter/JunitFormatterTest.php create mode 100644 test/unit/Formatter/junit.xsd diff --git a/src/Command/AssertBackwardsCompatible.php b/src/Command/AssertBackwardsCompatible.php index 3c68079f..6940b11b 100644 --- a/src/Command/AssertBackwardsCompatible.php +++ b/src/Command/AssertBackwardsCompatible.php @@ -13,6 +13,7 @@ use Roave\BackwardCompatibility\CompareApi; use Roave\BackwardCompatibility\Factory\ComposerInstallationReflectorFactory; use Roave\BackwardCompatibility\Formatter\GithubActionsFormatter; +use Roave\BackwardCompatibility\Formatter\JunitFormatter; use Roave\BackwardCompatibility\Formatter\MarkdownPipedToSymfonyConsoleFormatter; use Roave\BackwardCompatibility\Formatter\SymfonyConsoleTextFormatter; use Roave\BackwardCompatibility\Git\CheckedOutRepository; @@ -69,7 +70,7 @@ protected function configure(): void 'format', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, - 'Currently supports "console", "markdown" or "github-actions"', + 'Currently supports "console", "markdown", "github-actions" or "junit"', ['console'], ) ->addOption( @@ -82,7 +83,7 @@ protected function configure(): void <<<'USAGE' -Without arguments, this command will attempt to detect the +Without arguments, this command will attempt to detect the latest stable git tag ("release", according to this tool) of the repository in your CWD (current working directory), and will use it as baseline for the defined API. @@ -152,6 +153,7 @@ public function execute(InputInterface $input, OutputInterface $output): int 'console' => new SymfonyConsoleTextFormatter($stdErr), 'markdown' => new MarkdownPipedToSymfonyConsoleFormatter($output), 'github-actions' => new GithubActionsFormatter($output, $toPath), + 'junit' => new JunitFormatter($output, $toPath), ]; foreach ( @@ -159,6 +161,7 @@ public function execute(InputInterface $input, OutputInterface $output): int Type\literal_scalar('console'), Type\literal_scalar('markdown'), Type\literal_scalar('github-actions'), + Type\literal_scalar('junit'), ))->coerce((array) $input->getOption('format')) as $format ) { $formatters[$format]->write($changes); diff --git a/src/Formatter/JunitFormatter.php b/src/Formatter/JunitFormatter.php new file mode 100644 index 00000000..990dc94d --- /dev/null +++ b/src/Formatter/JunitFormatter.php @@ -0,0 +1,71 @@ +basePath->__toString() . '/'; + + $changeCount = count($changes); + + $testcases = []; + + foreach ($changes as $change) { + $filename = $change->file === null ? null : Str\replace($change->file, $basePath, ''); + + $name = implode(':', [ + $this->escape($filename ?? ''), + $this->escape((string) ($change->line ?? '')), + $this->escape((string) ($change->column ?? '')), + ]); + + $testcases[] = sprintf( + ' ', + $this->escape($name), + $this->escape(trim($change->__toString())), + ); + } + + $result = implode("\n", [ + '', + sprintf( + '', + $changeCount, + $changeCount, + ), + ...$testcases, + '', + ]); + + $this->output->writeLn($result); + } + + private function escape(string $value): string + { + return htmlspecialchars($value, ENT_XML1 | ENT_COMPAT, 'UTF-8'); + } +} diff --git a/test/unit/Formatter/JunitFormatterTest.php b/test/unit/Formatter/JunitFormatterTest.php new file mode 100644 index 00000000..26756c4f --- /dev/null +++ b/test/unit/Formatter/JunitFormatterTest.php @@ -0,0 +1,90 @@ +write(Changes::fromList( + Change::removed('foo', true), + Change::added('bar', false), + Change::changed('baz', false) + ->onFile('baz-file.php'), + Change::changed('tab', false) + ->onFile('tab-file.php') + ->onLine(5), + Change::changed('taz', false) + ->onFile('taz-file.php') + ->onLine(6) + ->onColumn(15), + Change::changed('tar', false) + ->onFile('tar-file.php') + ->onLine(-1) + ->onColumn(-1), + Change::changed('file-in-checked-out-dir', false) + ->onFile($temporaryLocation . '/foo/bar/subpath/file-in-checked-out-dir.php') + ->onLine(10) + ->onColumn(20), + )); + + Filesystem\delete_directory($temporaryLocation, true); + + $fetchedOutput = $output->fetch(); + + self::assertSame( + <<<'OUTPUT' + + + + + + + + + + + +OUTPUT + , + $fetchedOutput, + ); + + if (! extension_loaded('dom')) { + return; + } + + $dom = new DOMDocument(); + $dom->loadXML($fetchedOutput); + + self::assertTrue( + $dom->schemaValidate(__DIR__ . '/junit.xsd'), + ); + } +} diff --git a/test/unit/Formatter/junit.xsd b/test/unit/Formatter/junit.xsd new file mode 100644 index 00000000..b2ab4e9b --- /dev/null +++ b/test/unit/Formatter/junit.xsd @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 512629fd9572d52feb9823fca441800a368390ff Mon Sep 17 00:00:00 2001 From: Brad <28307684+mad-briller@users.noreply.github.com> Date: Wed, 12 Oct 2022 11:00:16 +0100 Subject: [PATCH 2/2] Resolved pull request feedback. --- src/Command/AssertBackwardsCompatible.php | 1 + src/Formatter/JunitFormatter.php | 51 +++++++++++----------- test/unit/Formatter/JunitFormatterTest.php | 2 +- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/Command/AssertBackwardsCompatible.php b/src/Command/AssertBackwardsCompatible.php index 6940b11b..3f2f83ad 100644 --- a/src/Command/AssertBackwardsCompatible.php +++ b/src/Command/AssertBackwardsCompatible.php @@ -72,6 +72,7 @@ protected function configure(): void InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Currently supports "console", "markdown", "github-actions" or "junit"', ['console'], + ['console', 'markdown', 'github-actions', 'junit'], ) ->addOption( 'install-development-dependencies', diff --git a/src/Formatter/JunitFormatter.php b/src/Formatter/JunitFormatter.php index 990dc94d..7d1b6b50 100644 --- a/src/Formatter/JunitFormatter.php +++ b/src/Formatter/JunitFormatter.php @@ -18,11 +18,16 @@ use const ENT_COMPAT; use const ENT_XML1; -class JunitFormatter implements OutputFormatter +/** + * String concatenatation is used rather than DOMDocument or simplexml + * as that would cause the formatter to be dependent on the DOM php + * extension, which may not be available in all environments. + */ +final class JunitFormatter implements OutputFormatter { public function __construct( - private OutputInterface $output, - private CheckedOutRepository $basePath, + private readonly OutputInterface $output, + private readonly CheckedOutRepository $basePath, ) { } @@ -32,39 +37,33 @@ public function write(Changes $changes): void $changeCount = count($changes); - $testcases = []; + $this->output->writeLn(''); + $this->output->writeLn(sprintf( + '', + $changeCount, + $changeCount, + )); foreach ($changes as $change) { $filename = $change->file === null ? null : Str\replace($change->file, $basePath, ''); - $name = implode(':', [ - $this->escape($filename ?? ''), - $this->escape((string) ($change->line ?? '')), - $this->escape((string) ($change->column ?? '')), - ]); + $name = $this->escapeXmlAttribute(implode(':', [ + $filename ?? '', + (string) ($change->line ?? ''), + (string) ($change->column ?? ''), + ])); - $testcases[] = sprintf( + $this->output->writeLn(sprintf( ' ', - $this->escape($name), - $this->escape(trim($change->__toString())), - ); + $this->escapeXmlAttribute($name), + $this->escapeXmlAttribute(trim($change->__toString())), + )); } - $result = implode("\n", [ - '', - sprintf( - '', - $changeCount, - $changeCount, - ), - ...$testcases, - '', - ]); - - $this->output->writeLn($result); + $this->output->writeLn(''); } - private function escape(string $value): string + private function escapeXmlAttribute(string $value): string { return htmlspecialchars($value, ENT_XML1 | ENT_COMPAT, 'UTF-8'); } diff --git a/test/unit/Formatter/JunitFormatterTest.php b/test/unit/Formatter/JunitFormatterTest.php index 26756c4f..b9261bde 100644 --- a/test/unit/Formatter/JunitFormatterTest.php +++ b/test/unit/Formatter/JunitFormatterTest.php @@ -61,7 +61,7 @@ public function testWrite(): void self::assertSame( <<<'OUTPUT' - +