Skip to content

Commit

Permalink
re-writes DefaultStrategy and adds unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
llaville committed Aug 23, 2024
1 parent 2243a8d commit bf84787
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 32 deletions.
90 changes: 58 additions & 32 deletions src/Composer/DefaultStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
*/
namespace Bartlett\BoxManifest\Composer;

use Bartlett\BoxManifest\Helper\ManifestFile;
use Bartlett\BoxManifest\Helper\ManifestFormat;

use InvalidArgumentException;
use function class_exists;
use function is_array;
use function sprintf;
use function str_ends_with;
use function str_starts_with;

/**
* This is the default strategy used to build your manifest(s).
Expand All @@ -26,40 +26,66 @@ public function __construct(private ManifestFactory $factory)
{
}

public function build(ManifestOptions $options): ?string
public function getCallable(string $outputFormat, ?string $resourceFile): callable
{
$factory = $this->factory;
if ('auto' == $outputFormat) {
if (null === $resourceFile) {
return [$this->factory, 'toConsole'];
}

$recognizedFilePatternsRules = [
'sbom.json' => [$this->factory, 'toSbomJson'],
'sbom.xml' => [$this->factory, 'toSbomXml'],
'.cdx.json' => [$this->factory, 'toSbomJson'],
'.cdx.xml' => [$this->factory, 'toSbomXml'],
'manifest.txt' => [$this->factory, 'toText'],
'plain.txt' => [$this->factory, 'toText'],
'ansi.txt' => [$this->factory, 'toHighlight'],
'console.txt' => [$this->factory, 'toConsole'],
'custom.bin' => [$this->factory, 'fromClass'],
];

foreach ($recognizedFilePatternsRules as $extension => $callable) {
if (str_ends_with($resourceFile, $extension)) {
return $callable;
}
}

throw new InvalidArgumentException(sprintf('Cannot auto-detect format for "%s" resource file', $resourceFile));
}

$recognizedOutputFormatRules = [
'console' => [$this->factory, 'toConsole'],
'ansi' => [$this->factory, 'toHighlight'],
'plain' => [$this->factory, 'toText'],
'sbom-json' => [$this->factory, 'toSbomJson'],
'sbom-xml' => [$this->factory, 'toSbomXml'],
];

foreach ($recognizedOutputFormatRules as $format => $callable) {
if ($outputFormat === $format) {
return $callable;
}
}

if (!class_exists($outputFormat)) {
throw new InvalidArgumentException(sprintf('Format "%s" is not supported', $outputFormat));
}

return [$this->factory, 'fromClass'];
}

public function build(ManifestOptions $options): ?string
{
/** @var string $rawFormat */
$rawFormat = $options->getFormat(true);
$format = $options->getFormat();
$outputFile = $options->getOutputFile();
$sbomSpec = $options->getSbomSpec();

$output = $outputFile ? ManifestFile::tryFrom(basename($outputFile)) : null;
$callable = $this->getCallable($rawFormat, $options->getOutputFile());

return match ($format) {
ManifestFormat::auto => match ($output) {
null, ManifestFile::ansi => $factory->toHighlight(),
ManifestFile::consoleTable => $factory->toConsole(),
ManifestFile::txt => $factory->toText(),
ManifestFile::sbomXml => $factory->toSbom('xml', $sbomSpec),
ManifestFile::sbomJson => $factory->toSbom('json', $sbomSpec),
default => match (pathinfo($outputFile ? : '', PATHINFO_EXTENSION)) {
'xml' => $factory->toSbom('xml', $sbomSpec),
'json' => $factory->toSbom('json', $sbomSpec),
'', 'txt' => $factory->toText(),
default => throw new InvalidArgumentException('Cannot auto-detect format with such output file')
}
},
ManifestFormat::plain => $factory->toText(),
ManifestFormat::ansi => $factory->toHighlight(),
ManifestFormat::console => $factory->toConsole(),
ManifestFormat::sbomXml => $factory->toSbom('xml', $sbomSpec),
ManifestFormat::sbomJson => $factory->toSbom('json', $sbomSpec),
default => class_exists($rawFormat)
? $factory->fromClass($rawFormat)
: throw new InvalidArgumentException(sprintf('Format "%s" is not supported', $rawFormat))
};
if (is_array($callable) && str_starts_with($callable[1], 'toSbom')) {
return $callable($options->getSbomSpec());
}

return $callable();
}
}
1 change: 1 addition & 0 deletions src/Helper/ManifestFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ enum ManifestFile: string
case custom = 'custom.bin';
case ansi = 'ansi.txt';
case txt = 'manifest.txt';
case plain = 'plain.txt';
case xml = 'manifest.xml';
case sbomXml = 'sbom.xml';
case sbomJson = 'sbom.json';
Expand Down
97 changes: 97 additions & 0 deletions tests/unit/DefaultStrategyTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php declare(strict_types=1);
/**
* This file is part of the BoxManifest package.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Bartlett\BoxManifest\Tests;

use Bartlett\BoxManifest\Composer\DefaultStrategy;
use Bartlett\BoxManifest\Composer\ManifestFactory;

use KevinGH\Box\Configuration\Configuration;

use stdClass;
use InvalidArgumentException;

/**
* Unit tests for DefaultStrategy component of the Box Manifest
*
* @author Laurent Laville
* @since Release 4.0.0
*/
final class DefaultStrategyTest extends TestCase
{
/**
* @dataProvider dpRecognizedFilePatterns
*/
public function testRecognizedFilePatterns(string $outputFormat, ?string $resource, bool $expectedException): void
{
$this->testAutoDetection($outputFormat, $resource, $expectedException);
}

/**
* @dataProvider dpRecognizedOutputFormat
*/
public function testRecognizedOutputFormat(string $outputFormat, ?string $resource, bool $expectedException): void
{
$this->testAutoDetection($outputFormat, $resource, $expectedException);
}

private function testAutoDetection(string $outputFormat, ?string $resource, bool $expectedException): void
{
if ($expectedException) {
$this->expectException(InvalidArgumentException::class);
}

$configFilePath = __DIR__ . '/../fixtures/phario-manifest-2.0.x-dev/box.json';

$raw = new stdClass();
$main = 'main';
$raw->{$main} = false;
$config = Configuration::create($configFilePath, $raw);

$factory = new ManifestFactory($config, true, '4.x-dev', '4.x-dev');
$strategy = new DefaultStrategy($factory);

$callable = $strategy->getCallable($outputFormat, $resource);
$this->assertIsCallable($callable);
}

public static function dpRecognizedFilePatterns(): iterable
{
yield ['auto', null, false];
yield ['auto', 'console.txt', false];
yield ['auto', 'plain.txt', false];
yield ['auto', 'manifest.txt', false];
yield ['auto', 'ansi.txt', false];
yield ['auto', 'sbom.json', false];
yield ['auto', 'sbom.xml', false];
yield ['auto', 'sbom.cdx.json', false];
yield ['auto', 'sbom.cdx.xml', false];
yield ['auto', 'custom.bin', false];
yield ['auto', 'custom.txt', true];
}

public static function dpRecognizedOutputFormat(): iterable
{
yield ['console', null, false];
yield ['console', 'whatever.you.want', false];

yield ['plain', null, false];
yield ['plain', 'whatever.you.want', false];

yield ['ansi', null, false];
yield ['ansi', 'whatever.you.want', false];

yield ['sbom-json', null, false];
yield ['sbom-json', 'whatever.you.want', false];

yield ['sbom-xml', null, false];
yield ['sbom-xml', 'whatever.you.want', false];

yield ['\My\Space\ClassNotFound', null, true];
yield ['\My\Space\ClassNotFound', 'whatever.you.want', true];
}
}

0 comments on commit bf84787

Please sign in to comment.