Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b675898
test: drop redundancy to avoid coverage gymnastics
NickSdot Jul 25, 2026
2a99cad
test: suppressed native warning
NickSdot Jul 25, 2026
5e1d15d
test: added coverage for UpstreamResolver
NickSdot Jul 25, 2026
d321870
test: added early return
NickSdot Jul 25, 2026
fb91944
test: increased coverage
NickSdot Jul 25, 2026
dd84480
test: increased edge case coverage
NickSdot Jul 25, 2026
adf1d61
test: added cwd fallback
NickSdot Jul 25, 2026
2febc56
test: added fixer mapping coverage
NickSdot Jul 25, 2026
aa177dd
test: removed obsolete todos
NickSdot Jul 25, 2026
882d504
test: simplified simpara source matching
NickSdot Jul 25, 2026
6ac4382
test: covered declaration masking
NickSdot Jul 25, 2026
6d5bbea
test: covered valid indentation styles
NickSdot Jul 25, 2026
710b2f6
test: covered exception name mismatch
NickSdot Jul 25, 2026
d4c1c48
test: covered report edge cases
NickSdot Jul 25, 2026
aa3efc1
test: covered input all paths
NickSdot Jul 25, 2026
9ccabf9
test: covered diff edge cases
NickSdot Jul 25, 2026
dc9867d
test: covered scope edge cases
NickSdot Jul 25, 2026
aadfd5f
test: covered noop fix edge cases
NickSdot Jul 25, 2026
4517f3d
test: covered fixer input edge cases
NickSdot Jul 25, 2026
ac4839c
test: academics
NickSdot Jul 25, 2026
7fde630
test: academics
NickSdot Jul 25, 2026
cd66b7e
test: covered processing failure paths
NickSdot Jul 25, 2026
6d19229
test: removed @api; now covered by tests
NickSdot Jul 25, 2026
c413dc7
review: removed yoda style
NickSdot Jul 26, 2026
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
3 changes: 2 additions & 1 deletion src/Diff/UpstreamResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@ public function resolve(string $repoRoot, string $repoName): ?string
}

try {
// filesystem or OS edge cases
if (!flock($lock, LOCK_EX)) {
return null;
return null; // @codeCoverageIgnore
}

return $this->refreshAndResolve($repoRoot, $repoName);
Expand Down
6 changes: 3 additions & 3 deletions src/Path/EntityResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,16 +123,16 @@ private function scanDirectory(string $directory): array
*/
private function resolveFile(string $filePath, array &$visited, array &$paths, ?string $originEntity = null): array
{
if (isset($visited[$filePath]) || !is_readable($filePath)) {
if (isset($visited[$filePath])) {
return [];
}

$visited[$filePath] = true;

$content = file_get_contents($filePath);
$content = @file_get_contents($filePath);

if ($content === false) {
return []; // @codeCoverageIgnore
return [];
}

$entities = $this->extractEntities($content, $filePath, $visited, $paths);
Expand Down
2 changes: 1 addition & 1 deletion src/Process/NativeProcessRunner.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ final class NativeProcessRunner implements ProcessRunnerInterface
{
public function run(array $command, string $workingDirectory, array $environment = []): ProcessResult
{
$process = proc_open(
$process = @proc_open(
$command,
[
['pipe', 'r'],
Expand Down
13 changes: 7 additions & 6 deletions src/Progress/ConsoleProgress.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,14 @@ public function finish(): void

private function drawBar(int $current, string $filePath, int $violations = 0): void
{
$percent = $this->totalFiles > 0
? (int)floor(($current / $this->totalFiles) * 100)
: 0; // @codeCoverageIgnore
if ($this->totalFiles === 0) {
// prevented by public methods
return; // @codeCoverageIgnore
}

$filled = $this->totalFiles > 0
? (int)floor(($current / $this->totalFiles) * self::BAR_WIDTH)
: 0; // @codeCoverageIgnore
$ratio = $current / $this->totalFiles;
$percent = (int)floor($ratio * 100);
$filled = (int)floor($ratio * self::BAR_WIDTH);

$empty = self::BAR_WIDTH - $filled;

Expand Down
6 changes: 1 addition & 5 deletions src/RelativePath.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,7 @@ final class RelativePath
{
public static function fromWorkingDirectory(string $filePath): string
{
$workingDirectory = getcwd();
if ($workingDirectory === false) {
return $filePath; // @codeCoverageIgnore
}

$workingDirectory = getcwd() ?: '.';
$prefix = rtrim(str_replace('\\', '/', $workingDirectory), '/') . '/';
$normalisedPath = str_replace('\\', '/', $filePath);

Expand Down
2 changes: 1 addition & 1 deletion src/Report/Report.php
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ public function getTotalWarningLevelViolationCount(): int
));
}

/** @api not implemented */
// not implemented
Comment thread
NickSdot marked this conversation as resolved.
public function getTotalInfoLevelViolationCount(): int
{
return array_sum(array_map(
Expand Down
4 changes: 2 additions & 2 deletions src/Report/Reporter/ConsoleReporter.php
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,11 @@ private function collectPerformanceRows(Report $report): array

private function formatSeverity(Severity $severity): string
{
return match ($severity) { // @codeCoverageIgnore
return match ($severity) {
Severity::ERROR => $this->red(str_pad(Severity::ERROR->name, 7)),
Severity::WARNING => $this->yellow(str_pad(Severity::WARNING->name, 7)),
default => $this->dim(str_pad(strtoupper($severity->name), 7)),
}; // @codeCoverageIgnore
};
}

private function formatPerformanceCell(?float $time, float $totalTime): string
Expand Down
7 changes: 0 additions & 7 deletions src/Runner/RunScopeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,17 +105,10 @@ private function absolutePaths(array $paths): array
private function expandReferencedTargets(array &$targets): void
{
$pending = array_keys($targets);
$visitedFiles = [];
$visitedEntityPaths = [];

for ($i = 0; isset($pending[$i]); $i++) {
$file = $pending[$i];

if (isset($visitedFiles[$file])) {
continue;
}

$visitedFiles[$file] = true;
$content = @file_get_contents($file);

if ($content === false) {
Expand Down
18 changes: 3 additions & 15 deletions src/Sniff/SimparaSniff.php
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ public function process(\DOMDocument $document, File $file): array
throw new \LogicException('Could not map simpara violation to source content.');
}

if ($match['selfClosing']) {
$closingOffset = $match['closingOffset'];
if (null === $closingOffset) {
continue;
}

Expand All @@ -160,11 +161,6 @@ public function process(\DOMDocument $document, File $file): array
continue;
}

$closingOffset = $match['closingOffset'];
if ($closingOffset === null) {
throw new \LogicException('Could not map simpara violation to source content.');
}

$affectedRanges = $this->elementNameRanges(
$file,
$match['beginOffset'],
Expand Down Expand Up @@ -219,13 +215,7 @@ private function getAllowedElements(): array
|> array_values(...);
}

/**
* @return list<array{
* beginOffset: int,
* selfClosing: bool,
* closingOffset: int|null
* }>
*/
/** @return list<array{beginOffset: int, closingOffset: int|null}> */
private function sourceMatches(File $file): array
{
preg_match_all(
Expand All @@ -245,7 +235,6 @@ private function sourceMatches(File $file): array
if (str_ends_with(rtrim($tag), '/>')) {
$sourceMatches[] = [
'beginOffset' => $offset,
'selfClosing' => true,
'closingOffset' => null,
];
continue;
Expand All @@ -262,7 +251,6 @@ private function sourceMatches(File $file): array

$sourceMatches[] = [
'beginOffset' => $opening,
'selfClosing' => false,
'closingOffset' => $offset,
];
}
Expand Down
5 changes: 5 additions & 0 deletions tests/Support/Sniff/ExposedAbstractSniff.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,9 @@ public function exposeSeverity(): Severity
{
return $this->severity;
}

public function exposeMaskNonElementMarkup(string $source): string
{
return $this->maskNonElementMarkup($source);
}
}
86 changes: 86 additions & 0 deletions tests/Unit/ApplicationInputTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,38 @@
use DocbookCS\Diff\DiffParser;
use DocbookCS\Diff\GitDiffProvider;
use DocbookCS\Diff\UpstreamResolver;
use DocbookCS\Fix\Fix;
use DocbookCS\Fix\FixApplier;
use DocbookCS\Fix\FixPlan;
use DocbookCS\Fix\FixResult;
use DocbookCS\Fix\Fixer\ExceptionNameFixer;
use DocbookCS\Git\GitClient;
use DocbookCS\Git\GitException;
use DocbookCS\Path\DiffPathLoader;
use DocbookCS\Path\EntityResolver;
use DocbookCS\Path\PathLoader;
use DocbookCS\Path\PathMatcher;
use DocbookCS\Progress\NullProgress;
use DocbookCS\Report\FileReport;
use DocbookCS\Report\Report;
use DocbookCS\Report\Reporter\ConsoleReporter;
use DocbookCS\Runner\EntityExpansionMarker;
use DocbookCS\Runner\EntityPreprocessor;
use DocbookCS\Runner\RunCoordinator;
use DocbookCS\Runner\RunMode;
use DocbookCS\Runner\RunPlan;
use DocbookCS\Runner\RunPlanner;
use DocbookCS\Runner\RunScope;
use DocbookCS\Runner\RunScopeResolver;
use DocbookCS\Runner\ViolationScopeFilter;
use DocbookCS\Runner\XmlFileProcessor;
use DocbookCS\Runner\XmlFixRunner;
use DocbookCS\Runner\XmlSniffRunner;
use DocbookCS\Sniff\AbstractSniff;
use DocbookCS\Sniff\ExceptionNameSniff;
use DocbookCS\Source\File;
use DocbookCS\Violation\SourceRange;
use DocbookCS\Violation\Violation;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\UsesClass;
Expand All @@ -46,22 +61,37 @@
UsesClass(DiffChangeset::class),
UsesClass(DiffParser::class),
UsesClass(DiffPathLoader::class),
UsesClass(EntityExpansionMarker::class),
UsesClass(EntityPreprocessor::class),
UsesClass(EntityResolver::class),
UsesClass(ExceptionNameFixer::class),
UsesClass(ExceptionNameSniff::class),
UsesClass(File::class),
UsesClass(FileReport::class),
UsesClass(Fix::class),
UsesClass(FixApplier::class),
UsesClass(FixPlan::class),
UsesClass(FixResult::class),
UsesClass(GitClient::class),
UsesClass(GitDiffProvider::class),
UsesClass(GitException::class),
UsesClass(NullProgress::class),
UsesClass(PathLoader::class),
UsesClass(PathMatcher::class),
UsesClass(Report::class),
UsesClass(RunCoordinator::class),
UsesClass(RunMode::class),
UsesClass(RunPlan::class),
UsesClass(RunPlanner::class),
UsesClass(RunScope::class),
UsesClass(RunScopeResolver::class),
UsesClass(SniffEntry::class),
UsesClass(SourceRange::class),
UsesClass(UpstreamResolver::class),
UsesClass(Violation::class),
UsesClass(ViolationScopeFilter::class),
UsesClass(XmlFileProcessor::class),
UsesClass(XmlFixRunner::class),
UsesClass(XmlSniffRunner::class),
]
final class ApplicationInputTest extends TestCase
Expand Down Expand Up @@ -119,6 +149,36 @@ public function itDetectsAPipedDiffWithoutAFlag(): void
self::assertSame('', $this->readStream($this->stderr));
}

#[Test]
public function itDetectsPipedDiffThroughCliEntryPoint(): void
{
$process = proc_open(
[PHP_BINARY, __DIR__ . '/../../bin/docbook-cs', '--config=' . self::VALID_CONFIG, self::SCAN_FILE],
[
['pipe', 'r'],
['pipe', 'w'],
['pipe', 'w'],
],
$pipes,
getcwd() ?: '.',
);
self::assertIsResource($process);
self::assertCount(3, $pipes);
self::assertIsResource($pipes[0]);
self::assertIsResource($pipes[1]);
self::assertIsResource($pipes[2]);

fclose($pipes[0]);
$stdout = stream_get_contents($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[1]);
fclose($pipes[2]);

self::assertSame(2, proc_close($process), $stdout ?: '');
self::assertIsString($stderr);
self::assertStringContainsString('Paths cannot be combined with diff input', $stderr);
}

#[Test]
public function itRejectsPathsCombinedWithAPipedDiff(): void
{
Expand Down Expand Up @@ -160,6 +220,32 @@ public function itAcceptsTheWideOption(): void
self::assertSame('', $this->readStream($this->stderr));
}

#[Test]
public function itAppliesFixesInFixMode(): void
{
$temporaryPath = tempnam(sys_get_temp_dir(), 'docbook-cs-');
self::assertIsString($temporaryPath);
$filePath = $temporaryPath . '.xml';
rename($temporaryPath, $filePath);
file_put_contents($filePath, '<root><classname>RuntimeException</classname></root>');

try {
$app = new Application(
['docbook-cs', '--config=' . self::VALID_CONFIG, '--fix', $filePath],
$this->stdout,
$this->stderr,
);

self::assertSame(0, $app->run());
self::assertSame(
'<root><exceptionname>RuntimeException</exceptionname></root>',
file_get_contents($filePath),
);
} finally {
@unlink($filePath);
}
}

/** @param resource $stream */
private function readStream(mixed $stream): string
{
Expand Down
Loading