Skip to content

Commit

Permalink
Ensure stdout is valid XML for junit format (#129)
Browse files Browse the repository at this point in the history
  • Loading branch information
janedbal committed Apr 22, 2024
1 parent 701b485 commit d37bcd0
Show file tree
Hide file tree
Showing 9 changed files with 148 additions and 104 deletions.
7 changes: 4 additions & 3 deletions bin/composer-dependency-analyser
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ spl_autoload_register(static function (string $class) use ($psr4Prefix): void {
/** @var non-empty-string $cwd */
$cwd = getcwd();

$printer = new Printer();
$initializer = new Initializer($cwd, $printer);
$stdOutPrinter = new Printer(STDOUT);
$stdErrPrinter = new Printer(STDERR);
$initializer = new Initializer($cwd, $stdOutPrinter, $stdErrPrinter);
$stopwatch = new Stopwatch();

try {
Expand All @@ -49,7 +50,7 @@ try {
InvalidConfigException |
InvalidCliException $e
) {
$printer->printLine("\n<red>{$e->getMessage()}</red>" . PHP_EOL);
$stdErrPrinter->printLine("\n<red>{$e->getMessage()}</red>" . PHP_EOL);
exit(255);
}

Expand Down
29 changes: 18 additions & 11 deletions src/Initializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,22 @@ class Initializer
/**
* @var Printer
*/
private $printer;
private $stdOutPrinter;

/**
* @var Printer
*/
private $stdErrPrinter;

public function __construct(
string $cwd,
Printer $printer
Printer $stdOutPrinter,
Printer $stdErrPrinter
)
{
$this->printer = $printer;
$this->cwd = $cwd;
$this->stdOutPrinter = $stdOutPrinter;
$this->stdErrPrinter = $stdErrPrinter;
}

/**
Expand All @@ -85,7 +92,7 @@ public function initConfiguration(
}

if (is_file($configPath)) {
$this->printer->printLine('<gray>Using config</gray> ' . $configPath);
$this->stdErrPrinter->printLine('<gray>Using config</gray> ' . $configPath);

try {
$config = (static function () use ($configPath) {
Expand Down Expand Up @@ -190,17 +197,17 @@ public function initComposerClassLoaders(): array
$loaders = ClassLoader::getRegisteredLoaders();

if (count($loaders) > 1) {
$this->printer->printLine("\nDetected multiple class loaders:");
$this->stdErrPrinter->printLine("\nDetected multiple class loaders:");

foreach ($loaders as $vendorDir => $_) {
$this->printer->printLine(" • <gray>$vendorDir</gray>");
$this->stdErrPrinter->printLine(" • <gray>$vendorDir</gray>");
}

$this->printer->printLine('');
$this->stdErrPrinter->printLine('');
}

if (count($loaders) === 0) {
$this->printer->printLine("\nNo composer class loader detected!\n");
$this->stdErrPrinter->printLine("\nNo composer class loader detected!\n");
}

return $loaders;
Expand All @@ -215,7 +222,7 @@ public function initCliOptions(string $cwd, array $argv): CliOptions
$cliOptions = (new Cli($cwd, $argv))->getProvidedOptions();

if ($cliOptions->help !== null) {
$this->printer->printLine(self::$help);
$this->stdOutPrinter->printLine(self::$help);
throw new InvalidCliException(''); // just exit
}

Expand All @@ -229,11 +236,11 @@ public function initFormatter(CliOptions $options): ResultFormatter
{
switch ($options->format) {
case 'junit':
return new JunitFormatter($this->cwd, $this->printer);
return new JunitFormatter($this->cwd, $this->stdOutPrinter);

case 'console':
case null:
return new ConsoleFormatter($this->cwd, $this->printer);
return new ConsoleFormatter($this->cwd, $this->stdOutPrinter);

default:
throw new InvalidConfigException("Invalid format option provided, allowed are 'console' or 'junit'.");
Expand Down
23 changes: 21 additions & 2 deletions src/Printer.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

namespace ShipMonk\ComposerDependencyAnalyser;

use LogicException;
use function array_keys;
use function array_values;
use function fwrite;
use function str_replace;
use const PHP_EOL;

Expand All @@ -21,14 +23,31 @@ class Printer
'</gray>' => "\033[0m",
];

/**
* @var resource
*/
private $resource;

/**
* @param resource $resource
*/
public function __construct($resource)
{
$this->resource = $resource;
}

public function printLine(string $string): void
{
echo $this->colorize($string) . PHP_EOL;
$this->print($string . PHP_EOL);
}

public function print(string $string): void
{
echo $this->colorize($string);
$result = fwrite($this->resource, $this->colorize($string));

if ($result === false) {
throw new LogicException('Could not write to output stream.');
}
}

private function colorize(string $string): string
Expand Down
45 changes: 29 additions & 16 deletions tests/BinTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,30 @@ public function test(): void
$okOutput = 'No composer issues found';
$helpOutput = 'Usage:';

$usingConfig = 'Using config';

$junitOutput = '<?xml version="1.0" encoding="UTF-8"?><testsuites></testsuites>';

$this->runCommand('php bin/composer-dependency-analyser', $rootDir, 0, $okOutput);
$this->runCommand('php bin/composer-dependency-analyser --verbose', $rootDir, 0, $okOutput);
$this->runCommand('php ../bin/composer-dependency-analyser', $testsDir, 255, $noComposerJsonError);
$this->runCommand('php bin/composer-dependency-analyser', $rootDir, 0, $okOutput, $usingConfig);
$this->runCommand('php bin/composer-dependency-analyser --verbose', $rootDir, 0, $okOutput, $usingConfig);
$this->runCommand('php ../bin/composer-dependency-analyser', $testsDir, 255, null, $noComposerJsonError);
$this->runCommand('php bin/composer-dependency-analyser --help', $rootDir, 255, $helpOutput);
$this->runCommand('php ../bin/composer-dependency-analyser --help', $testsDir, 255, $helpOutput);
$this->runCommand('php bin/composer-dependency-analyser --composer-json=composer.json', $rootDir, 0, $okOutput);
$this->runCommand('php bin/composer-dependency-analyser --composer-json=composer.lock', $rootDir, 255, $noPackagesError);
$this->runCommand('php bin/composer-dependency-analyser --composer-json=README.md', $rootDir, 255, $parseError);
$this->runCommand('php ../bin/composer-dependency-analyser --composer-json=composer.json', $testsDir, 255, $noComposerJsonError);
$this->runCommand('php ../bin/composer-dependency-analyser --composer-json=../composer.json --config=../composer-dependency-analyser.php', $testsDir, 0, $okOutput);
$this->runCommand('php bin/composer-dependency-analyser --composer-json=composer.json --format=console', $rootDir, 0, $okOutput);
$this->runCommand('php bin/composer-dependency-analyser --composer-json=composer.json --format=junit', $rootDir, 0, $junitOutput);
$this->runCommand('php bin/composer-dependency-analyser --composer-json=composer.json', $rootDir, 0, $okOutput, $usingConfig);
$this->runCommand('php bin/composer-dependency-analyser --composer-json=composer.lock', $rootDir, 255, null, $noPackagesError);
$this->runCommand('php bin/composer-dependency-analyser --composer-json=README.md', $rootDir, 255, null, $parseError);
$this->runCommand('php ../bin/composer-dependency-analyser --composer-json=composer.json', $testsDir, 255, null, $noComposerJsonError);
$this->runCommand('php ../bin/composer-dependency-analyser --composer-json=../composer.json --config=../composer-dependency-analyser.php', $testsDir, 0, $okOutput, $usingConfig);
$this->runCommand('php bin/composer-dependency-analyser --composer-json=composer.json --format=console', $rootDir, 0, $okOutput, $usingConfig);
$this->runCommand('php bin/composer-dependency-analyser --composer-json=composer.json --format=junit', $rootDir, 0, $junitOutput, $usingConfig);
}

private function runCommand(
string $command,
string $cwd,
int $expectedExitCode,
string $expectedOutputContains
?string $expectedOutputContains = null,
?string $expectedErrorContains = null
): void
{
$desc = [
Expand Down Expand Up @@ -74,11 +77,21 @@ private function runCommand(
$extraInfo
);

self::assertStringContainsString(
$expectedOutputContains,
$output,
$extraInfo
);
if ($expectedOutputContains !== null) {
self::assertStringContainsString(
$expectedOutputContains,
$output,
$extraInfo
);
}

if ($expectedErrorContains !== null) {
self::assertStringContainsString(
$expectedErrorContains,
$errorOutput,
$extraInfo
);
}
}

}
38 changes: 8 additions & 30 deletions tests/ConsoleFormatterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,24 @@

namespace ShipMonk\ComposerDependencyAnalyser;

use Closure;
use PHPUnit\Framework\TestCase;
use ShipMonk\ComposerDependencyAnalyser\Config\Configuration;
use ShipMonk\ComposerDependencyAnalyser\Config\ErrorType;
use ShipMonk\ComposerDependencyAnalyser\Config\Ignore\UnusedErrorIgnore;
use ShipMonk\ComposerDependencyAnalyser\Result\AnalysisResult;
use ShipMonk\ComposerDependencyAnalyser\Result\ConsoleFormatter;
use ShipMonk\ComposerDependencyAnalyser\Result\ResultFormatter;
use ShipMonk\ComposerDependencyAnalyser\Result\SymbolUsage;
use function ob_get_clean;
use function ob_start;
use function preg_replace;
use function str_replace;

class ConsoleFormatterTest extends TestCase
class ConsoleFormatterTest extends FormatterTest
{

public function testPrintResult(): void
{
// editorconfig-checker-disable
$formatter = new ConsoleFormatter('/app', new Printer());

$noIssuesOutput = $this->captureAndNormalizeOutput(static function () use ($formatter): void {
$noIssuesOutput = $this->getFormatterNormalizedOutput(static function (ResultFormatter $formatter): void {
$formatter->format(new AnalysisResult(2, 0.123, [], [], [], [], [], [], [], []), new CliOptions(), new Configuration());
});
$noIssuesButUnusedIgnores = $this->captureAndNormalizeOutput(static function () use ($formatter): void {
$noIssuesButUnusedIgnores = $this->getFormatterNormalizedOutput(static function (ResultFormatter $formatter): void {
$formatter->format(new AnalysisResult(2, 0.123, [], [], [], [], [], [], [], [new UnusedErrorIgnore(ErrorType::SHADOW_DEPENDENCY, null, null)]), new CliOptions(), new Configuration());
});

Expand Down Expand Up @@ -79,10 +72,10 @@ public function testPrintResult(): void
[]
);

$regularOutput = $this->captureAndNormalizeOutput(static function () use ($formatter, $analysisResult): void {
$regularOutput = $this->getFormatterNormalizedOutput(static function ($formatter) use ($analysisResult): void {
$formatter->format($analysisResult, new CliOptions(), new Configuration());
});
$verboseOutput = $this->captureAndNormalizeOutput(static function () use ($formatter, $analysisResult): void {
$verboseOutput = $this->getFormatterNormalizedOutput(static function ($formatter) use ($analysisResult): void {
$options = new CliOptions();
$options->verbose = true;
$formatter->format($analysisResult, $options, new Configuration());
Expand Down Expand Up @@ -206,24 +199,9 @@ public function testPrintResult(): void
// editorconfig-checker-enable
}

private function removeColors(string $output): string
{
return (string) preg_replace('#\\x1b[[][^A-Za-z]*[A-Za-z]#', '', $output);
}

/**
* @param Closure(): void $closure
*/
private function captureAndNormalizeOutput(Closure $closure): string
{
ob_start();
$closure();
return $this->normalizeEol((string) ob_get_clean());
}

private function normalizeEol(string $string): string
protected function createFormatter(Printer $printer): ResultFormatter
{
return str_replace("\r\n", "\n", $string);
return new ConsoleFormatter('/app', $printer);
}

}
43 changes: 43 additions & 0 deletions tests/FormatterTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php declare(strict_types = 1);

namespace ShipMonk\ComposerDependencyAnalyser;

use Closure;
use PHPUnit\Framework\TestCase;
use ShipMonk\ComposerDependencyAnalyser\Result\ResultFormatter;
use function fopen;
use function preg_replace;
use function str_replace;
use function stream_get_contents;

abstract class FormatterTest extends TestCase
{

abstract protected function createFormatter(Printer $printer): ResultFormatter;

/**
* @param Closure(ResultFormatter): void $closure
*/
protected function getFormatterNormalizedOutput(Closure $closure): string
{
$stream = fopen('php://memory', 'w');
self::assertNotFalse($stream);

$printer = new Printer($stream);
$formatter = $this->createFormatter($printer);

$closure($formatter);
return $this->normalizeEol((string) stream_get_contents($stream, -1, 0));
}

protected function normalizeEol(string $string): string
{
return str_replace("\r\n", "\n", $string);
}

protected function removeColors(string $output): string
{
return (string) preg_replace('#\\x1b[[][^A-Za-z]*[A-Za-z]#', '', $output);
}

}
12 changes: 6 additions & 6 deletions tests/InitializerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public function testInitConfiguration(): void
$options = new CliOptions();
$options->ignoreUnknownClasses = true;

$initializer = new Initializer(__DIR__, $printer);
$initializer = new Initializer(__DIR__, $printer, $printer);
$config = $initializer->initConfiguration($options, $composerJson);

self::assertEquals([new PathToScan(__DIR__, false)], $config->getPathsToScan());
Expand All @@ -44,7 +44,7 @@ public function testInitComposerJson(): void
$options = new CliOptions();
$options->composerJson = 'sample.json';

$initializer = new Initializer($cwd, $printer);
$initializer = new Initializer($cwd, $printer, $printer);
$composerJson = $initializer->initComposerJson($options);

self::assertSame(
Expand All @@ -70,7 +70,7 @@ public function testInitComposerJsonWithAbsolutePath(): void
$options = new CliOptions();
$options->composerJson = $composerJsonPath;

$initializer = new Initializer($cwd, $printer);
$initializer = new Initializer($cwd, $printer, $printer);
$composerJson = $initializer->initComposerJson($options);

self::assertSame(
Expand All @@ -90,7 +90,7 @@ public function testInitCliOptions(): void
{
$printer = $this->createMock(Printer::class);

$initializer = new Initializer(__DIR__, $printer);
$initializer = new Initializer(__DIR__, $printer, $printer);
$options = $initializer->initCliOptions(__DIR__, ['script.php', '--verbose']);

self::assertNull($options->showAllUsages);
Expand All @@ -108,7 +108,7 @@ public function testInitCliOptionsHelp(): void
{
$printer = $this->createMock(Printer::class);

$initializer = new Initializer(__DIR__, $printer);
$initializer = new Initializer(__DIR__, $printer, $printer);

$this->expectException(InvalidCliException::class);
$initializer->initCliOptions(__DIR__, ['script.php', '--help']);
Expand All @@ -118,7 +118,7 @@ public function testInitFormatter(): void
{
$printer = $this->createMock(Printer::class);

$initializer = new Initializer(__DIR__, $printer);
$initializer = new Initializer(__DIR__, $printer, $printer);

$optionsNoFormat = new CliOptions();
self::assertInstanceOf(ConsoleFormatter::class, $initializer->initFormatter($optionsNoFormat));
Expand Down
Loading

0 comments on commit d37bcd0

Please sign in to comment.