Skip to content

Commit e6a64da

Browse files
committed
Merge branch 'review'
2 parents c650789 + 8c5b15b commit e6a64da

File tree

9 files changed

+549
-719
lines changed

9 files changed

+549
-719
lines changed

phpunit.xml.dist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
</exclude>
3333
<report>
3434
<html outputDirectory="build/coverage" />
35+
<php outputFile="build/coverage.php" />
3536
</report>
3637
</coverage>
3738
<extensions>

scripts/check-coverage.php

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
#!/usr/bin/env php
2+
<?php declare(strict_types=1);
3+
4+
use Salient\Cli\CliApplication;
5+
use Salient\Utility\Exception\FilesystemErrorException;
6+
use SebastianBergmann\CodeCoverage\Node\Directory;
7+
use SebastianBergmann\CodeCoverage\CodeCoverage;
8+
9+
require dirname(__DIR__) . '/vendor/autoload.php';
10+
11+
$app = new CliApplication(dirname(__DIR__));
12+
13+
/** @var string[] */
14+
$args = $_SERVER['argv'];
15+
$file = $args[1] ?? dirname(__DIR__) . '/build/coverage.php';
16+
if (!is_file($file)) {
17+
throw new FilesystemErrorException(sprintf('File not found: %s', $file));
18+
}
19+
20+
/** @var CodeCoverage */
21+
$coverage = require ($file);
22+
23+
// [ Directory => [ name, API state, `Contract` state, code state ], ... ]
24+
$components = [
25+
'Contract' => ['Contracts', false, false, false], // 2025-02-22: top-level interfaces finalised
26+
'Utility' => ['Utils', true, false, '2025-01-28: code review finalised'],
27+
'Polyfill' => ['Polyfills', false, true, '2025-03-04: code review finalised'],
28+
'Collection' => ['Collections', true, true, '2025-02-23: code review finalised'],
29+
'Core' => ['Core', true, true, '2025-02-14: code review finalised, replacement of `Legacy` classes still pending'],
30+
'Iterator' => ['Iterators', true, true, '2025-02-26: code review finalised'],
31+
'Cache' => ['Cache', true, true, '2025-02-23: code review finalised'],
32+
'Console' => ['Console', null, null, null],
33+
'Container' => ['Container', null, null, null],
34+
'Http' => ['HTTP', null, null, null],
35+
'Db' => ['Db', null, null, null],
36+
'Cli' => ['CLI', null, null, null],
37+
'Sync' => ['Sync', null, null, null],
38+
'Curler' => ['Curler', true, true, '2025-02-24: code review finalised'],
39+
'PHPDoc' => ['PHPDoc', null, false, null],
40+
'PHPStan' => ['PHPStan', false, false, '2025-03-04: code review finalised'],
41+
'Testing' => ['Testing', true, false, '2025-03-04: code review finalised'],
42+
'Sli' => ['Sli', null, false, null],
43+
];
44+
45+
/**
46+
* @return array{coverage:float,executed:int,executable:int,code:int,comments:int,total:int}
47+
*/
48+
function getLines(Directory $dir): array
49+
{
50+
$lines = $dir->linesOfCode();
51+
$lines = [
52+
'coverage' => 1.0,
53+
'executed' => $dir->numberOfExecutedLines(),
54+
'executable' => $dir->numberOfExecutableLines(),
55+
'code' => $lines['nonCommentLinesOfCode'],
56+
'comments' => $lines['commentLinesOfCode'],
57+
'total' => $lines['linesOfCode'],
58+
];
59+
if ($lines['executable'] > 0) {
60+
$lines['coverage'] = $lines['executed'] / $lines['executable'];
61+
}
62+
return $lines;
63+
}
64+
65+
/**
66+
* @param non-empty-array<non-empty-array<string|int>> $table
67+
*/
68+
function printTable(array $table): void
69+
{
70+
foreach ($table as $row) {
71+
foreach ($row as $i => $column) {
72+
$widths[$i] = max($widths[$i] ?? 0, mb_strlen((string) $column));
73+
}
74+
}
75+
76+
$row = [];
77+
foreach ($widths as $width) {
78+
$row[] = str_repeat('-', $width);
79+
}
80+
array_splice($table, 1, 0, [$row]);
81+
82+
foreach ($table as $row) {
83+
printf('|');
84+
foreach ($row as $i => $column) {
85+
printf(
86+
' %s%s |',
87+
$column,
88+
str_repeat(' ', $widths[$i] - mb_strlen((string) $column)),
89+
);
90+
}
91+
printf("\n");
92+
}
93+
}
94+
95+
$report = $coverage->getReport();
96+
$totalLines = getLines($report);
97+
$dirLines = [];
98+
/** @var Directory $dir */
99+
foreach ($report->directories() as $dir) {
100+
$dirLines[$dir->name()] = getLines($dir);
101+
}
102+
103+
$data = [['Component', 'Size', 'Coverage', 'Lines', 'Code', 'Executable', 'API?', 'Contracts?', 'Code?']];
104+
$totalSize = 0;
105+
$progress = [0, 0, 0];
106+
$expectedProgress = [0, 0, 0];
107+
foreach ($components as $dir => $component) {
108+
$lines = $dirLines[$dir];
109+
$name = array_shift($component);
110+
$size = $lines['code'] / $totalLines['code'];
111+
$totalSize += $size;
112+
$row = [
113+
$name,
114+
sprintf('%.2f%%', $size * 100),
115+
sprintf('%.2f%%', $lines['coverage'] * 100),
116+
$lines['total'],
117+
$lines['code'],
118+
$lines['executable'],
119+
];
120+
$size = $lines['total'] / $totalLines['total'];
121+
foreach ($component as $i => $state) {
122+
$row[] = $state === null
123+
? ''
124+
: ($state === false
125+
? '-'
126+
: '');
127+
if ($state !== false) {
128+
$expectedProgress[$i] += $size;
129+
if ($state !== null) {
130+
$progress[$i] += $size;
131+
}
132+
}
133+
}
134+
$data[] = $row;
135+
}
136+
137+
$row = [
138+
'TOTAL',
139+
sprintf('%.2f%%', $totalSize * 100),
140+
sprintf('%.2f%%', $totalLines['coverage'] * 100),
141+
$totalLines['total'],
142+
$totalLines['code'],
143+
$totalLines['executable'],
144+
];
145+
foreach ($progress as $i => $actualProgress) {
146+
$row[] = sprintf('%.2f%%', $actualProgress * 100 / $expectedProgress[$i]);
147+
}
148+
$data[] = $row;
149+
150+
printTable($data);

src/Toolkit/Cache/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
`salient/cache` provides a key-value store backed by a SQLite database.
1515

1616
- Implements [PSR-16 (Common Interface for Caching Libraries)][PSR-16]
17-
- Multiple cache operations can be grouped into an atomic transaction via a
18-
[time-bound instance of the cache][asOfNow] that maintains an exclusive lock
19-
on the underlying database until it goes out of scope or is explicitly closed
17+
- Multiple operations can be grouped into an atomic transaction via a
18+
[time-bound copy of the cache][asOfNow] that maintains an exclusive lock on
19+
the underlying database until it goes out of scope or is explicitly closed
2020

2121
[asOfNow]:
2222
https://salient-labs.github.io/toolkit/Salient.Cache.CacheStore.html#_asOfNow

src/Toolkit/Polyfill/README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@
1111

1212
---
1313

14-
`salient/polyfills` provides the following polyfills:
15-
16-
- `PhpToken` for PHP 7.4
14+
`salient/polyfills` provides `PhpToken` and `Stringable` on PHP 7.4.
1715

1816
## Documentation
1917

src/Toolkit/Testing/Console/MockTarget.php

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -77,25 +77,6 @@ public function getEol(): string
7777
return "\n";
7878
}
7979

80-
/**
81-
* @inheritDoc
82-
*/
83-
public function close(): void
84-
{
85-
if (!$this->IsValid) {
86-
return;
87-
}
88-
89-
$this->IsStdout = false;
90-
$this->IsStderr = false;
91-
$this->IsTty = false;
92-
$this->Width = null;
93-
$this->Stream = null;
94-
unset($this->Formatter);
95-
96-
$this->IsValid = false;
97-
}
98-
9980
/**
10081
* @inheritDoc
10182
*/
@@ -129,10 +110,10 @@ public function write(int $level, string $message, array $context = []): void
129110
$this->assertIsValid();
130111

131112
if ($this->Stream) {
132-
$suffix = $message === '' || $message[-1] !== "\r"
133-
? "\n"
134-
: '';
135-
File::write($this->Stream, $message . $suffix);
113+
$suffix = $message !== '' && $message[-1] === "\r"
114+
? ''
115+
: "\n";
116+
File::writeAll($this->Stream, $message . $suffix);
136117
}
137118

138119
$message = [$level, $message];
@@ -142,6 +123,25 @@ public function write(int $level, string $message, array $context = []): void
142123
$this->Messages[] = $message;
143124
}
144125

126+
/**
127+
* @inheritDoc
128+
*/
129+
public function close(): void
130+
{
131+
if (!$this->IsValid) {
132+
return;
133+
}
134+
135+
$this->IsStdout = false;
136+
$this->IsStderr = false;
137+
$this->IsTty = false;
138+
$this->Width = null;
139+
$this->Stream = null;
140+
unset($this->Formatter);
141+
142+
$this->IsValid = false;
143+
}
144+
145145
/**
146146
* Get messages written to the target and flush its message cache
147147
*

src/Toolkit/Testing/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@
1313

1414
`salient/testing` provides classes that are useful in test suites.
1515

16+
- `MockPhpStream` preserves data written to `php://` streams for subsequent
17+
reading via the same URI
18+
- `MockTarget` records messages logged via the Salient toolkit's [Console
19+
API][salient/console]
20+
1621
## Documentation
1722

1823
[API documentation][api-docs] for `salient/testing` tracks the `main` branch of
@@ -21,4 +26,5 @@ be found.
2126

2227
[api-docs]:
2328
https://salient-labs.github.io/toolkit/namespace-Salient.Testing.html
29+
[salient/console]: https://packagist.org/packages/salient/console
2430
[toolkit]: https://github.com/salient-labs/toolkit

tests/unit/Toolkit/Polyfill/NativePhpTokenTest.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,8 @@
1111
*/
1212
final class NativePhpTokenTest extends PhpTokenTestCase
1313
{
14-
protected static string $Token = PhpToken::class;
14+
protected static function getToken(): string
15+
{
16+
return PhpToken::class;
17+
}
1518
}

tests/unit/Toolkit/Polyfill/PhpTokenTest.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,8 @@
99
*/
1010
final class PhpTokenTest extends PhpTokenTestCase
1111
{
12-
protected static string $Token = PhpToken::class;
12+
protected static function getToken(): string
13+
{
14+
return PhpToken::class;
15+
}
1316
}

0 commit comments

Comments
 (0)