Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions config/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,8 @@ services:

DaveLiddament\StaticAnalysisResultsBaseliner\:
resource: '../src/*'
# Value objects and exceptions are not services. Excluding them means a newly type hinted
# value object fails at container compile time rather than at runtime.
exclude:
- '../src/**/*Exception.php'
- '../src/Domain/Common/*'
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace DaveLiddament\StaticAnalysisResultsBaseliner\Domain\OutputFormatter;

final class InvalidOutputFormatterException extends \Exception
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Common\SarbException;

final class InvalidOutputFormatterException extends SarbException
{
}
17 changes: 17 additions & 0 deletions src/Domain/Pruner/PrunedResults.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,21 @@ public function getInputAnalysisResults(): AnalysisResults
{
return $this->inputAnalysisResults;
}

/**
* Returns the input results that matched the baseline (i.e. input results minus the pruned ones).
*/
public function getBaseLinedResults(): AnalysisResults
{
$prunedAnalysisResults = $this->prunedResults->getAnalysisResults();

$baseLinedResults = [];
foreach ($this->inputAnalysisResults->getAnalysisResults() as $analysisResult) {
if (!in_array($analysisResult, $prunedAnalysisResults, true)) {
$baseLinedResults[] = $analysisResult;
}
}

return new AnalysisResults($baseLinedResults);
}
}
2 changes: 1 addition & 1 deletion src/Domain/Utils/FqcnRemover.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ final class FqcnRemover
/**
* Removes anything that looks like a FQCN from the string.
*/
public function removeRqcn(string $raw): string
public function removeFqcn(string $raw): string
{
$parts = explode(' ', $raw);
$outputParts = [];
Expand Down
10 changes: 0 additions & 10 deletions src/Domain/Utils/StringUtils.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,6 @@ public static function removeFromStart(string $needle, string $haystack): string
return substr($haystack, $length);
}

/**
* Returns true if $haystack ends with $needle.
*/
public static function endsWith(string $needle, string $haystack): bool
{
$length = strlen($needle);

return substr($haystack, -$length) === $needle;
}

/**
* Returns true if line is empty (or contains only white space).
*/
Expand Down
4 changes: 2 additions & 2 deletions src/Framework/Command/ListResultsParsesCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,15 @@ protected function execute(InputInterface $input, OutputInterface $output): int

$output->writeln(
sprintf(
' Create baseline: <info>%s | sarb create --input-format="%s"</info>',
' Create baseline: <info>%s | sarb create --input-format="%s" <baseline-file></info>',
$identifier->getToolCommand(),
$identifier->getCode(),
),
);

$output->writeln(
sprintf(
' Remove baseline results: <info>%s | sarb remove</info>',
' Remove baseline results: <info>%s | sarb remove <baseline-file></info>',
$identifier->getToolCommand(),
),
);
Expand Down
10 changes: 8 additions & 2 deletions src/Framework/Command/RemoveBaseLineFromResultsCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ protected function configure(): void
{
$this->setDescription('Shows issues created since the baseline');

// The documentation tells users to run "sarb remove". That currently works because
// Symfony matches unambiguous abbreviations, but a real alias means adding another
// remove-* command in the future can not silently break every user's CI.
$this->setAliases(['remove']);

$outputFormatters = $this->outputFormatterLookupService->getIdentifiers();
$this->addOption(
self::OUTPUT_FORMAT,
Expand Down Expand Up @@ -123,8 +128,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int

$returnCode = $outputAnalysisResults->hasNoIssues() ? 0 : 1;

if ($showRandomIssues && !$prunedResults->getInputAnalysisResults()->hasNoIssues()) {
$randomIssues = $this->randomResultsPicker->getRandomResultsToFix($prunedResults->getInputAnalysisResults());
$baseLinedResults = $prunedResults->getBaseLinedResults();
if ($showRandomIssues && !$baseLinedResults->hasNoIssues()) {
$randomIssues = $this->randomResultsPicker->getRandomResultsToFix($baseLinedResults);

OutputWriter::writeToStdError(
$output,
Expand Down
11 changes: 11 additions & 0 deletions src/Framework/Container/internal/AddCommandCompilerPass.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

namespace DaveLiddament\StaticAnalysisResultsBaseliner\Framework\Container\internal;

use Composer\InstalledVersions;
use DaveLiddament\StaticAnalysisResultsBaseliner\Framework\Container\Container;
use Symfony\Component\Console\Application;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
Expand All @@ -18,14 +19,24 @@

final class AddCommandCompilerPass implements CompilerPassInterface
{
private const PACKAGE_NAME = 'dave-liddament/sarb';

public function process(ContainerBuilder $container): void
{
$container->register(Application::class, Application::class);
$definition = $container->getDefinition(Application::class);
$definition->setArguments(['SARB', $this->getVersion()]);
$definition->setPublic(true);
$taggedServices = $container->findTaggedServiceIds(Container::COMMAND_TAG);
foreach (array_keys($taggedServices) as $id) {
$definition->addMethodCall('add', [new Reference($id)]);
}
}

private function getVersion(): string
{
return InstalledVersions::isInstalled(self::PACKAGE_NAME)
? (InstalledVersions::getPrettyVersion(self::PACKAGE_NAME) ?? 'UNKNOWN')
: 'UNKNOWN';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
* Handles PHPStan's JSON output.
*
* NOTE: SARB only deals with errors that are attached to a particular line in a file.
* PHPStan can report general errors (not specific to file). These are ignored by SARB.
* PHPStan can report general errors (not specific to a file). If any of these are present SARB
* stops with an error, as they normally indicate the PHPStan run itself failed.
*/
final class PhpstanJsonResultsParser implements ResultsParser
{
Expand Down Expand Up @@ -121,7 +122,7 @@ private function convertAnalysisResultFromArray(
}

$rawType = ArrayUtils::getStringValue($analysisResultAsArray, self::TYPE);
$type = $this->fqcnRemover->removeRqcn($rawType);
$type = $this->fqcnRemover->removeFqcn($rawType);

$location = Location::fromAbsoluteFileName(
$absoluteFileName,
Expand Down
3 changes: 1 addition & 2 deletions tests/Integration/EndToEndTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Common\ProjectRoot;
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Common\RelativeFileName;
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Utils\Path;
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Utils\StringUtils;
use DaveLiddament\StaticAnalysisResultsBaseliner\Framework\Command\CreateBaseLineCommand;
use DaveLiddament\StaticAnalysisResultsBaseliner\Framework\Command\ListHistoryAnalysersCommand;
use DaveLiddament\StaticAnalysisResultsBaseliner\Framework\Command\ListResultsParsesCommand;
Expand Down Expand Up @@ -406,7 +405,7 @@ private function updatePathsInJsonFiles(ProjectRoot $projectRoot): void
$files = scandir($directory);
$this->assertNotFalse($files);
foreach ($files as $file) {
if (StringUtils::endsWith('.json', $file)) {
if (str_ends_with($file, '.json')) {
$fullPath = Path::makeAbsolute($file, $directory);
$contents = file_get_contents($fullPath);
$this->assertNotFalse($contents);
Expand Down
88 changes: 88 additions & 0 deletions tests/Unit/Core/Pruner/PrunedResultsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?php

namespace DaveLiddament\StaticAnalysisResultsBaseliner\Tests\Unit\Core\Pruner;

use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Common\BaseLine;
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Common\ProjectRoot;
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Common\Severity;
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Pruner\PrunedResults;
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\ResultsParser\AnalysisResult;
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\ResultsParser\AnalysisResults;
use DaveLiddament\StaticAnalysisResultsBaseliner\Plugins\GitDiffHistoryAnalyser\GitCommit;
use DaveLiddament\StaticAnalysisResultsBaseliner\Plugins\ResultsParsers\SarbJsonResultsParser\SarbJsonResultsParser;
use DaveLiddament\StaticAnalysisResultsBaseliner\Tests\Helpers\AnalysisResultsAdderTrait;
use DaveLiddament\StaticAnalysisResultsBaseliner\Tests\Helpers\BaseLineResultsBuilder;
use DaveLiddament\StaticAnalysisResultsBaseliner\Tests\TestDoubles\HistoryFactoryStub;
use PHPUnit\Framework\TestCase;

final class PrunedResultsTest extends TestCase
{
use AnalysisResultsAdderTrait;

/**
* @var AnalysisResult
*/
private $baseLinedResult;

/**
* @var AnalysisResult
*/
private $newResult;

protected function setUp(): void
{
$projectRoot = ProjectRoot::fromCurrentWorkingDirectory('/');
$this->baseLinedResult = $this->buildAnalysisResult($projectRoot, '/FILE_1', 1, 'TYPE_1', Severity::error());
$this->newResult = $this->buildAnalysisResult($projectRoot, '/FILE_2', 2, 'TYPE_2', Severity::error());
}

public function testGetters(): void
{
$baseLine = $this->createBaseLine();
$prunedAnalysisResults = new AnalysisResults([$this->newResult]);
$inputAnalysisResults = new AnalysisResults([$this->baseLinedResult, $this->newResult]);

$prunedResults = new PrunedResults($baseLine, $prunedAnalysisResults, $inputAnalysisResults);

$this->assertSame($baseLine, $prunedResults->getBaseLine());
$this->assertSame($prunedAnalysisResults, $prunedResults->getPrunedResults());
$this->assertSame($inputAnalysisResults, $prunedResults->getInputAnalysisResults());
}

public function testBaseLinedResultsAreInputResultsMinusPrunedResults(): void
{
$prunedResults = new PrunedResults(
$this->createBaseLine(),
new AnalysisResults([$this->newResult]),
new AnalysisResults([$this->baseLinedResult, $this->newResult]),
);

$baseLinedResults = $prunedResults->getBaseLinedResults();

$this->assertSame([$this->baseLinedResult], $baseLinedResults->getAnalysisResults());
}

public function testNoBaseLinedResultsWhenAllInputResultsAreNew(): void
{
$prunedResults = new PrunedResults(
$this->createBaseLine(),
new AnalysisResults([$this->baseLinedResult, $this->newResult]),
new AnalysisResults([$this->baseLinedResult, $this->newResult]),
);

$this->assertTrue($prunedResults->getBaseLinedResults()->hasNoIssues());
}

private function createBaseLine(): BaseLine
{
$baseLineResultsBuilder = new BaseLineResultsBuilder();
$baseLineResultsBuilder->add('FILE_1', 1, 'TYPE_1', Severity::error());

return new BaseLine(
new HistoryFactoryStub(),
$baseLineResultsBuilder->build(),
new SarbJsonResultsParser(),
new GitCommit('fae40b3d596780ffd746dbd2300d05dcfbd09033'),
);
}
}
2 changes: 1 addition & 1 deletion tests/Unit/Core/Utils/FqcnRemoverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public static function dataProvider(): array
public function testFqcnRemover(string $input, string $expected): void
{
$fqcnRemove = new FqcnRemover();
$actual = $fqcnRemove->removeRqcn($input);
$actual = $fqcnRemove->removeFqcn($input);
$this->assertEquals($expected, $actual);
}
}
15 changes: 0 additions & 15 deletions tests/Unit/Core/Utils/StringUtilsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,6 @@ public function testRemoveFromStartExceptionWhenInvalidStart(): void
StringUtils::removeFromStart('Bar', 'FooBar');
}

public function testDoesEndWith1(): void
{
$this->assertTrue(StringUtils::endsWith('e', 'abcde'));
}

public function testDoesEndWith2(): void
{
$this->assertTrue(StringUtils::endsWith('de', 'abcde'));
}

public function testDoesNotEndWith1(): void
{
$this->assertFalse(StringUtils::endsWith('d', 'abcde'));
}

public function testIsEmpty(): void
{
$this->assertTrue(StringUtils::isEmptyLine(''));
Expand Down
10 changes: 10 additions & 0 deletions tools/Phpat/LayerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ public function testDomainDoesNotDependOnOtherLayers(): Rule
;
}

public function testDomainDoesNotDependOnSymfony(): Rule
{
return PHPat::rule()
->classes(Selector::inNamespace(self::DOMAIN_NAMESPACE))
->shouldNotDependOn()
->classes(Selector::inNamespace('Symfony'))
->because('Domain code should not depend on the framework')
;
}

public function testsPluginDoesNotDependOnFrameworkLayer(): Rule
{
return PHPat::rule()
Expand Down
Loading