Skip to content

Commit

Permalink
Add ability to define a custom mapper for the baseline store/load for…
Browse files Browse the repository at this point in the history
… people that need/want to store the baseline differently.

2 use-cases immediately come to mind:
- qossmic#45 (comment)
- using different format for storing violation in the PHP configuration (currently you still need to load violations from a yaml file even when using PHP config file for Deptrac)
  • Loading branch information
patrickkusebauch committed Jan 11, 2025
1 parent 4707e55 commit 5497d3c
Show file tree
Hide file tree
Showing 9 changed files with 121 additions and 24 deletions.
6 changes: 5 additions & 1 deletion config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Qossmic\Deptrac\Contract\Config\CollectorType;
use Qossmic\Deptrac\Contract\Config\EmitterType;
use Qossmic\Deptrac\Contract\Layer\LayerProvider;
use Qossmic\Deptrac\Contract\OutputFormatter\BaselineMapperInterface;
use Qossmic\Deptrac\Core\Analyser\DependencyLayersAnalyser;
use Qossmic\Deptrac\Core\Analyser\EventHandler\AllowDependencyHandler;
use Qossmic\Deptrac\Core\Analyser\EventHandler\DependsOnDisallowedLayer;
Expand Down Expand Up @@ -109,6 +110,7 @@
use Qossmic\Deptrac\Supportive\OutputFormatter\MermaidJSOutputFormatter;
use Qossmic\Deptrac\Supportive\OutputFormatter\TableOutputFormatter;
use Qossmic\Deptrac\Supportive\OutputFormatter\XMLOutputFormatter;
use Qossmic\Deptrac\Supportive\OutputFormatter\YamlBaselineMapper;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\EventDispatcher\EventDispatcher;

Expand Down Expand Up @@ -404,11 +406,13 @@
->set(UnmatchedSkippedViolations::class)
->tag('kernel.event_subscriber')
;
$services->set(EventHelper::class)
$services->set(YamlBaselineMapper::class)
->args([
'$skippedViolations' => param('skip_violations'),
])
;
$services->alias(BaselineMapperInterface::class, YamlBaselineMapper::class);
$services->set(EventHelper::class);
$services
->set(DependencyLayersAnalyser::class)
;
Expand Down
10 changes: 7 additions & 3 deletions src/Contract/Analyser/EventHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Qossmic\Deptrac\Contract\Analyser;

use Qossmic\Deptrac\Contract\Layer\LayerProvider;
use Qossmic\Deptrac\Contract\OutputFormatter\BaselineMapperInterface;
use Qossmic\Deptrac\Contract\Result\SkippedViolation;
use Qossmic\Deptrac\Contract\Result\Violation;

Expand All @@ -19,13 +20,16 @@ final class EventHelper
private array $unmatchedSkippedViolation;

/**
* @param array<string, list<string>> $skippedViolations
* @var array<string, list<string>>
*/
private readonly array $skippedViolations;

public function __construct(
private readonly array $skippedViolations,
public readonly LayerProvider $layerProvider,
private readonly BaselineMapperInterface $baselineMapper,
) {
$this->unmatchedSkippedViolation = $skippedViolations;
$this->skippedViolations = $this->baselineMapper->loadViolations();
$this->unmatchedSkippedViolation = $this->skippedViolations;
}

/**
Expand Down
23 changes: 23 additions & 0 deletions src/Contract/OutputFormatter/BaselineMapperInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace Qossmic\Deptrac\Contract\OutputFormatter;

interface BaselineMapperInterface
{
/**
* Maps a grouped list of violations to a format that will be stored to a
* file by the `baseline` formatter.
*
* @param array<string,list<string>> $groupedViolations
*/
public function fromPHPListToString(array $groupedViolations): string;

/**
* Load the existing violation to ignore by custom mapper logic.
*
* @return array<string,list<string>>
*/
public function loadViolations(): array;
}
16 changes: 6 additions & 10 deletions src/Supportive/OutputFormatter/BaselineOutputFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@

namespace Qossmic\Deptrac\Supportive\OutputFormatter;

use Qossmic\Deptrac\Contract\OutputFormatter\BaselineMapperInterface;
use Qossmic\Deptrac\Contract\OutputFormatter\OutputFormatterInput;
use Qossmic\Deptrac\Contract\OutputFormatter\OutputFormatterInterface;
use Qossmic\Deptrac\Contract\OutputFormatter\OutputInterface;
use Qossmic\Deptrac\Contract\Result\OutputResult;
use Qossmic\Deptrac\Contract\Result\SkippedViolation;
use Qossmic\Deptrac\Contract\Result\Violation;
use Symfony\Component\Yaml\Yaml;

use function array_values;
use function ksort;
Expand All @@ -23,6 +23,10 @@ final class BaselineOutputFormatter implements OutputFormatterInterface
{
private const DEFAULT_PATH = './deptrac.baseline.yaml';

public function __construct(
private readonly BaselineMapperInterface $baselineMapper,
) {}

public static function getName(): string
{
return 'baseline';
Expand All @@ -49,15 +53,7 @@ public function finish(
}
file_put_contents(
$baselineFile,
Yaml::dump(
[
'deptrac' => [
'skip_violations' => $groupedViolations,
],
],
4,
2
)
$this->baselineMapper->fromPHPListToString($groupedViolations),
);
$output->writeLineFormatted('<info>Baseline dumped to '.realpath($baselineFile).'</info>');
}
Expand Down
36 changes: 36 additions & 0 deletions src/Supportive/OutputFormatter/YamlBaselineMapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace Qossmic\Deptrac\Supportive\OutputFormatter;

use Qossmic\Deptrac\Contract\OutputFormatter\BaselineMapperInterface;
use Symfony\Component\Yaml\Yaml;

final class YamlBaselineMapper implements BaselineMapperInterface
{
/**
* @param array<string, list<string>> $skippedViolations
*/
public function __construct(
private readonly array $skippedViolations,
) {}

public function fromPHPListToString(array $groupedViolations): string
{
return Yaml::dump(
[
'deptrac' => [
'skip_violations' => $groupedViolations,
],
],
4,
2
);
}

public function loadViolations(): array
{
return $this->skippedViolations;
}
}
35 changes: 33 additions & 2 deletions tests/Contract/Analyser/EventHelperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use PHPUnit\Framework\TestCase;
use Qossmic\Deptrac\Contract\Analyser\EventHelper;
use Qossmic\Deptrac\Contract\Layer\LayerProvider;
use Qossmic\Deptrac\Contract\OutputFormatter\BaselineMapperInterface;
use Qossmic\Deptrac\Core\Ast\AstMap\ClassLike\ClassLikeToken;

final class EventHelperTest extends TestCase
Expand All @@ -24,7 +25,22 @@ public function testIsViolationSkipped(): void
'DependencyClass2',
],
];
$helper = new EventHelper($configuration, new LayerProvider([]));

$baselineMapper = new class($configuration) implements BaselineMapperInterface {
public function __construct(private readonly array $violations) {}

public function fromPHPListToString(array $groupedViolations): string
{
return '';
}

public function loadViolations(): array
{
return $this->violations;
}
};

$helper = new EventHelper(new LayerProvider([]), $baselineMapper);

self::assertTrue(
$helper->shouldViolationBeSkipped(
Expand Down Expand Up @@ -78,7 +94,22 @@ public function testUnmatchedSkippedViolations(): void
'DependencyClass2',
],
];
$helper = new EventHelper($configuration, new LayerProvider([]));

$baselineMapper = new class($configuration) implements BaselineMapperInterface {
public function __construct(private readonly array $violations) {}

public function fromPHPListToString(array $groupedViolations): string
{
return '';
}

public function loadViolations(): array
{
return $this->violations;
}
};

$helper = new EventHelper(new LayerProvider([]), $baselineMapper);

self::assertTrue(
$helper->shouldViolationBeSkipped(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use Qossmic\Deptrac\Core\Ast\AstMap\ClassLike\ClassLikeToken;
use Qossmic\Deptrac\Core\Ast\AstMap\ClassLike\ClassLikeType;
use Qossmic\Deptrac\Core\Dependency\Dependency;
use Qossmic\Deptrac\Supportive\OutputFormatter\YamlBaselineMapper;

final class DependsOnInternalTokenTest extends TestCase
{
Expand Down Expand Up @@ -56,7 +57,7 @@ private function makeEvent(

public function testInvoke(): void
{
$helper = new EventHelper([], new LayerProvider([]));
$helper = new EventHelper(new LayerProvider([]), new YamlBaselineMapper([]));
$handler = new DependsOnInternalToken($helper, ['internal_tag' => '@layer-internal']);

$event = $this->makeEvent([], []);
Expand Down Expand Up @@ -103,7 +104,7 @@ public function testInvoke(): void

public function testDefaultInternalTag(): void
{
$helper = new EventHelper([], new LayerProvider([]));
$helper = new EventHelper(new LayerProvider([]), new YamlBaselineMapper([]));
$handler = new DependsOnInternalToken($helper, ['internal_tag' => null]);

$event = $this->makeEvent([], ['@internal' => ['']]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Qossmic\Deptrac\Core\Ast\AstMap\ClassLike\ClassLikeToken;
use Qossmic\Deptrac\Core\Ast\AstMap\ClassLike\ClassLikeType;
use Qossmic\Deptrac\Core\Dependency\Dependency;
use Qossmic\Deptrac\Supportive\OutputFormatter\YamlBaselineMapper;

final class DependsOnPrivateLayerTest extends TestCase
{
Expand Down Expand Up @@ -52,7 +53,7 @@ private function makeEvent(

public function testNoViolationsWhenDependentLayerIsPublic(): void
{
$helper = new EventHelper([], new LayerProvider([]));
$helper = new EventHelper(new LayerProvider([]), new YamlBaselineMapper([]));
$handler = new DependsOnPrivateLayer($helper);

$event = $this->makeEvent('DependerLayer', 'DependentLayer', true);
Expand All @@ -72,7 +73,7 @@ public function testNoViolationsWhenDependentLayerIsPublic(): void

public function testPropagationContinuesWhenPrivateLayerDependsOnItself(): void
{
$helper = new EventHelper([], new LayerProvider([]));
$helper = new EventHelper(new LayerProvider([]), new YamlBaselineMapper([]));
$handler = new DependsOnPrivateLayer($helper);

$event = $this->makeEvent('LayerA', 'LayerA', false);
Expand All @@ -92,7 +93,7 @@ public function testPropagationContinuesWhenPrivateLayerDependsOnItself(): void

public function testPropagationContinuesWhenPublicLayerDependsOnItself(): void
{
$helper = new EventHelper([], new LayerProvider([]));
$helper = new EventHelper(new LayerProvider([]), new YamlBaselineMapper([]));
$handler = new DependsOnPrivateLayer($helper);

$event = $this->makeEvent('layerA', 'layerA', true);
Expand All @@ -112,7 +113,7 @@ public function testPropagationContinuesWhenPublicLayerDependsOnItself(): void

public function testPropagationStoppedWhenDependingOnPrivateLayer(): void
{
$helper = new EventHelper([], new LayerProvider([]));
$helper = new EventHelper(new LayerProvider([]), new YamlBaselineMapper([]));
$handler = new DependsOnPrivateLayer($helper);

$event = $this->makeEvent('DependerLayer', 'DependentLayer', false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use Qossmic\Deptrac\Supportive\Console\Symfony\Style;
use Qossmic\Deptrac\Supportive\Console\Symfony\SymfonyOutput;
use Qossmic\Deptrac\Supportive\OutputFormatter\BaselineOutputFormatter;
use Qossmic\Deptrac\Supportive\OutputFormatter\YamlBaselineMapper;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Console\Style\SymfonyStyle;
Expand All @@ -31,7 +32,7 @@ class BaselineOutputFormatterTest extends TestCase
{
public function testGetName(): void
{
static::assertSame('baseline', (new BaselineOutputFormatter())->getName());
static::assertSame('baseline', (new BaselineOutputFormatter(new YamlBaselineMapper([])))->getName());
}

public static function basicDataProvider(): iterable
Expand Down Expand Up @@ -130,7 +131,7 @@ public function testBasic(array $rules, string $expectedOutput): void
try {
$output = new BufferedOutput();

$formatter = new BaselineOutputFormatter();
$formatter = new BaselineOutputFormatter(new YamlBaselineMapper([]));
$formatter->finish(
OutputResult::fromAnalysisResult($analysisResult),
$this->createSymfonyOutput($output),
Expand Down

0 comments on commit 5497d3c

Please sign in to comment.