diff --git a/bin/composer-dependency-analyser b/bin/composer-dependency-analyser
index e86fdde..459fba9 100755
--- a/bin/composer-dependency-analyser
+++ b/bin/composer-dependency-analyser
@@ -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 {
@@ -49,7 +50,7 @@ try {
InvalidConfigException |
InvalidCliException $e
) {
- $printer->printLine("\n{$e->getMessage()}" . PHP_EOL);
+ $stdErrPrinter->printLine("\n{$e->getMessage()}" . PHP_EOL);
exit(255);
}
diff --git a/src/Initializer.php b/src/Initializer.php
index 756d47a..c7c63ca 100644
--- a/src/Initializer.php
+++ b/src/Initializer.php
@@ -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;
}
/**
@@ -85,7 +92,7 @@ public function initConfiguration(
}
if (is_file($configPath)) {
- $this->printer->printLine('Using config ' . $configPath);
+ $this->stdErrPrinter->printLine('Using config ' . $configPath);
try {
$config = (static function () use ($configPath) {
@@ -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(" • $vendorDir");
+ $this->stdErrPrinter->printLine(" • $vendorDir");
}
- $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;
@@ -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
}
@@ -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'.");
diff --git a/src/Printer.php b/src/Printer.php
index 69b5e51..dc66842 100644
--- a/src/Printer.php
+++ b/src/Printer.php
@@ -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;
@@ -21,14 +23,31 @@ class Printer
'' => "\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
diff --git a/tests/BinTest.php b/tests/BinTest.php
index bf3b84c..ac5f6ee 100644
--- a/tests/BinTest.php
+++ b/tests/BinTest.php
@@ -23,27 +23,30 @@ public function test(): void
$okOutput = 'No composer issues found';
$helpOutput = 'Usage:';
+ $usingConfig = 'Using config';
+
$junitOutput = '';
- $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 = [
@@ -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
+ );
+ }
}
}
diff --git a/tests/ConsoleFormatterTest.php b/tests/ConsoleFormatterTest.php
index cdac5fc..0f200be 100644
--- a/tests/ConsoleFormatterTest.php
+++ b/tests/ConsoleFormatterTest.php
@@ -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());
});
@@ -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());
@@ -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);
}
}
diff --git a/tests/FormatterTest.php b/tests/FormatterTest.php
new file mode 100644
index 0000000..48e6ea1
--- /dev/null
+++ b/tests/FormatterTest.php
@@ -0,0 +1,43 @@
+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);
+ }
+
+}
diff --git a/tests/InitializerTest.php b/tests/InitializerTest.php
index aaad61e..ac6961f 100644
--- a/tests/InitializerTest.php
+++ b/tests/InitializerTest.php
@@ -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());
@@ -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(
@@ -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(
@@ -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);
@@ -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']);
@@ -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));
diff --git a/tests/JunitFormatterTest.php b/tests/JunitFormatterTest.php
index fdec353..995e2f0 100644
--- a/tests/JunitFormatterTest.php
+++ b/tests/JunitFormatterTest.php
@@ -2,33 +2,27 @@
namespace ShipMonk\ComposerDependencyAnalyser;
-use Closure;
use DOMDocument;
-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\JunitFormatter;
+use ShipMonk\ComposerDependencyAnalyser\Result\ResultFormatter;
use ShipMonk\ComposerDependencyAnalyser\Result\SymbolUsage;
-use function ob_get_clean;
-use function ob_start;
-use function str_replace;
use function trim;
use const LIBXML_NOEMPTYTAG;
-class JunitFormatterTest extends TestCase
+class JunitFormatterTest extends FormatterTest
{
public function testPrintResult(): void
{
// editorconfig-checker-disable
- $formatter = new JunitFormatter('/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());
});
@@ -79,10 +73,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());
@@ -170,21 +164,6 @@ public function testPrintResult(): void
// editorconfig-checker-enable
}
- /**
- * @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
- {
- return str_replace("\r\n", "\n", $string);
- }
-
private function prettyPrintXml(string $inputXml): string
{
$dom = new DOMDocument();
@@ -198,4 +177,9 @@ private function prettyPrintXml(string $inputXml): string
return trim($outputXml);
}
+ protected function createFormatter(Printer $printer): ResultFormatter
+ {
+ return new JunitFormatter('/app', $printer);
+ }
+
}
diff --git a/tests/PrinterTest.php b/tests/PrinterTest.php
index 4b37fcc..e89e02c 100644
--- a/tests/PrinterTest.php
+++ b/tests/PrinterTest.php
@@ -3,6 +3,8 @@
namespace ShipMonk\ComposerDependencyAnalyser;
use PHPUnit\Framework\TestCase;
+use function fopen;
+use function stream_get_contents;
use const PHP_EOL;
class PrinterTest extends TestCase
@@ -10,18 +12,15 @@ class PrinterTest extends TestCase
public function testPrintLine(): void
{
- $printer = new Printer();
+ $stream = fopen('php://memory', 'w');
+ self::assertNotFalse($stream);
- $this->expectOutputString("Hello, \033[31mworld\033[0m!" . PHP_EOL);
- $printer->printLine('Hello, world!');
- }
+ $printer = new Printer($stream);
- public function testPrint(): void
- {
- $printer = new Printer();
+ $printer->printLine('Hello, world!');
+ $printer->print('New line!');
- $this->expectOutputString("Hello, \033[31mworld\033[0m!");
- $printer->print('Hello, world!');
+ self::assertSame("Hello, \033[31mworld\033[0m!" . PHP_EOL . 'New line!', stream_get_contents($stream, -1, 0));
}
}