Skip to content

Commit d1e71c4

Browse files
Merge pull request #426 from eliashaeussler/task/compact-progress
[TASK] Make CompactProgressHandler display a more compact progress
2 parents f6b3e28 + 24c5341 commit d1e71c4

File tree

4 files changed

+87
-59
lines changed

4 files changed

+87
-59
lines changed

src/Http/Message/Handler/CompactProgressHandler.php

Lines changed: 59 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@
2727
use Symfony\Component\Console;
2828
use Throwable;
2929

30+
use function min;
31+
use function round;
32+
use function sprintf;
33+
use function str_pad;
34+
use function str_repeat;
35+
use function strlen;
36+
3037
/**
3138
* CompactProgressHandler.
3239
*
@@ -35,63 +42,86 @@
3542
*/
3643
final class CompactProgressHandler implements ResponseHandler
3744
{
38-
private const PROGRESS_BAR_FORMAT = ' %current%/%max% [%bar%] %percent:3s%% -- <%fail_tag%>%fail_count% %failures%</>';
45+
private const MAX_LINE_LENGTH = 80;
3946

40-
private readonly Console\Helper\ProgressBar $progressBar;
41-
private int $failures = 0;
47+
private readonly int $maxColumns;
48+
private int $columns = 0;
49+
private int $current = 0;
4250

4351
public function __construct(
4452
private readonly Console\Output\OutputInterface $output,
45-
int $max,
53+
private readonly int $max,
4654
) {
47-
$this->progressBar = $this->createProgressBar($output, $max);
55+
$this->maxColumns = $this->calculateMaxColumns();
4856
}
4957

5058
public function startProgressBar(): void
5159
{
52-
$this->progressBar->setMessage('info', 'fail_tag');
53-
$this->progressBar->setMessage('no', 'fail_count');
54-
$this->progressBar->setMessage('failures', 'failures');
55-
$this->progressBar->start();
60+
$this->reset();
5661
}
5762

5863
public function finishProgressBar(): void
5964
{
60-
$this->progressBar->finish();
61-
$this->output->writeln('');
65+
$this->reset();
6266
}
6367

6468
public function onSuccess(Message\ResponseInterface $response, Message\UriInterface $uri): void
6569
{
66-
$this->progressBar->advance();
67-
$this->progressBar->display();
70+
$this->advance('.');
6871
}
6972

7073
public function onFailure(Throwable $exception, Message\UriInterface $uri): void
7174
{
72-
$this->progressBar->setMessage((string) ++$this->failures, 'fail_count');
75+
$this->advance('<error>F</error>');
76+
}
7377

74-
if ($this->failures > 0) {
75-
$this->progressBar->setMessage('error', 'fail_tag');
76-
}
77-
if (1 === $this->failures) {
78-
$this->progressBar->setMessage('failure', 'failures');
78+
private function advance(string $output): void
79+
{
80+
++$this->columns;
81+
++$this->current;
82+
83+
if ($this->columns >= $this->maxColumns || $this->current === $this->max) {
84+
$this->output->writeln($output.$this->renderCurrentState());
85+
$this->columns = 0;
86+
} else {
87+
$this->output->write($output);
7988
}
80-
if (2 === $this->failures) {
81-
$this->progressBar->setMessage('failures', 'failures');
89+
}
90+
91+
private function renderCurrentState(): string
92+
{
93+
$percent = round(($this->current / $this->max) * 100);
94+
95+
if ($this->max === $this->current) {
96+
$fill = str_repeat(' ', $this->maxColumns - $this->columns);
97+
} else {
98+
$fill = '';
8299
}
83100

84-
$this->progressBar->advance();
85-
$this->progressBar->display();
101+
return sprintf(
102+
'%s %s / %d (%s%%)',
103+
$fill,
104+
str_pad((string) $this->current, strlen((string) $this->max), ' ', STR_PAD_LEFT),
105+
$this->max,
106+
str_pad((string) $percent, 3, ' ', STR_PAD_LEFT),
107+
);
86108
}
87109

88-
private function createProgressBar(Console\Output\OutputInterface $output, int $max): Console\Helper\ProgressBar
110+
private function calculateMaxColumns(): int
89111
{
90-
Console\Helper\ProgressBar::setFormatDefinition('cache-warmup', self::PROGRESS_BAR_FORMAT);
91-
92-
$progressBar = new Console\Helper\ProgressBar($output, $max);
93-
$progressBar->setFormat('cache-warmup');
112+
// current / max (...%)
113+
$stateLength = 2 * strlen((string) $this->max) + 11;
114+
$lineLength = min(
115+
(new Console\Terminal())->getWidth(),
116+
self::MAX_LINE_LENGTH,
117+
);
118+
119+
return $lineLength - $stateLength;
120+
}
94121

95-
return $progressBar;
122+
private function reset(): void
123+
{
124+
$this->columns = 0;
125+
$this->current = 0;
96126
}
97127
}

tests/unit/Command/CacheWarmupCommandTest.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ public function initializeResolvesRelativeConfigFilePath(): void
8888

8989
$this->commandTester->execute(['--config' => 'tests/unit/Fixtures/ConfigFiles/valid_config.php']);
9090

91-
self::assertStringContainsString('3/3', $this->commandTester->getDisplay());
91+
self::assertStringContainsString('3 / 3', $this->commandTester->getDisplay());
9292
}
9393

9494
#[Framework\Attributes\Test]
@@ -100,7 +100,7 @@ public function initializeLoadsConfigFromGivenFile(string $configFile): void
100100

101101
$this->commandTester->execute(['--config' => $configFile, '--progress' => true]);
102102

103-
self::assertStringContainsString('3/3', $this->commandTester->getDisplay());
103+
self::assertStringContainsString('3 / 3', $this->commandTester->getDisplay());
104104
}
105105

106106
#[Framework\Attributes\Test]
@@ -113,7 +113,7 @@ public function initializeOverwritesConfigFromGivenFileWithCommandOptions(): voi
113113
'--limit' => 1,
114114
]);
115115

116-
self::assertStringContainsString('1/1', $this->commandTester->getDisplay());
116+
self::assertStringContainsString('1 / 1', $this->commandTester->getDisplay());
117117
}
118118

119119
#[Framework\Attributes\Test]
@@ -129,7 +129,7 @@ public function initializeOverwritesConfigFromGivenFileDefinedAsEnvironmentVaria
129129

130130
putenv('CACHE_WARMUP_CONFIG');
131131

132-
self::assertStringContainsString('1/1', $this->commandTester->getDisplay());
132+
self::assertStringContainsString('1 / 1', $this->commandTester->getDisplay());
133133
}
134134

135135
#[Framework\Attributes\Test]
@@ -181,7 +181,7 @@ public function initializeOverwritesConfigFromGivenFileAndCommandOptionsWithEnvi
181181

182182
putenv('CACHE_WARMUP_LIMIT');
183183

184-
self::assertStringContainsString('2/2', $this->commandTester->getDisplay());
184+
self::assertStringContainsString('2 / 2', $this->commandTester->getDisplay());
185185
}
186186

187187
#[Framework\Attributes\Test]

tests/unit/Crawler/OutputtingCrawlerTest.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,8 +254,7 @@ public function crawlWritesCrawlingStateAsProgressBarToOutput(): void
254254
$output = $this->output->fetch();
255255

256256
self::assertNotEmpty($output);
257-
self::assertMatchesRegularExpression('#^\s*1/2 \S+\s+\d+% -- no failures#m', $output);
258-
self::assertMatchesRegularExpression('#^\s*2/2 \S+\s+\d+% -- 1 failure#m', $output);
257+
self::assertStringContainsString('2 / 2', $output);
259258
}
260259

261260
#[Framework\Attributes\Test]

tests/unit/Http/Message/Handler/CompactProgressHandlerTest.php

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -44,50 +44,53 @@ final class CompactProgressHandlerTest extends Framework\TestCase
4444
protected function setUp(): void
4545
{
4646
$this->output = new Console\Output\BufferedOutput();
47-
$this->subject = new Src\Http\Message\Handler\CompactProgressHandler($this->output, 10);
47+
$this->subject = new Src\Http\Message\Handler\CompactProgressHandler($this->output, 200);
4848
}
4949

5050
#[Framework\Attributes\Test]
51-
public function startProgressBarStartsProgressBar(): void
51+
public function onSuccessAdvancesProgressByOneStep(): void
5252
{
53-
$this->subject->startProgressBar();
53+
$response = new Psr7\Response();
54+
$uri = new Psr7\Uri('https://www.example.com');
5455

55-
$output = $this->output->fetch();
56+
$this->subject->startProgressBar();
57+
$this->subject->onSuccess($response, $uri);
5658

57-
self::assertNotEmpty($output);
58-
self::assertMatchesRegularExpression('#^\s*0/10 \S+\s+0% -- no failures#m', $output);
59+
self::assertSame('.', $this->output->fetch());
5960
}
6061

6162
#[Framework\Attributes\Test]
62-
public function finishProgressBarFinishesProgressBar(): void
63+
public function onSuccessPrintsCurrentStateOnLineBreak(): void
6364
{
65+
$response = new Psr7\Response();
66+
$uri = new Psr7\Uri('https://www.example.com');
67+
6468
$this->subject->startProgressBar();
65-
$this->subject->finishProgressBar();
6669

67-
$output = $this->output->fetch();
70+
for ($i = 0; $i < 100; ++$i) {
71+
$this->subject->onSuccess($response, $uri);
72+
}
6873

69-
self::assertNotEmpty($output);
70-
self::assertMatchesRegularExpression('#^\s*0/10 \S+\s+0% -- no failures#m', $output);
71-
self::assertMatchesRegularExpression('#^\s*10/10 \S+\s+100% -- no failures#m', $output);
74+
self::assertStringContainsString('63 / 200 ( 32%)', $this->output->fetch());
7275
}
7376

7477
#[Framework\Attributes\Test]
75-
public function onSuccessPrintsSuccessfulUrlAndAdvancesProgressBarByOneStep(): void
78+
public function onSuccessPrintsFinalStateOnFinishedCrawling(): void
7679
{
7780
$response = new Psr7\Response();
7881
$uri = new Psr7\Uri('https://www.example.com');
7982

8083
$this->subject->startProgressBar();
81-
$this->subject->onSuccess($response, $uri);
8284

83-
$output = $this->output->fetch();
85+
for ($i = 0; $i < 200; ++$i) {
86+
$this->subject->onSuccess($response, $uri);
87+
}
8488

85-
self::assertNotEmpty($output);
86-
self::assertMatchesRegularExpression('#^\s*1/10 \S+\s+10% -- no failures#m', $output);
89+
self::assertStringContainsString('200 / 200 (100%)', $this->output->fetch());
8790
}
8891

8992
#[Framework\Attributes\Test]
90-
public function onFailurePrintsFailedUrlAndAdvancesProgressBarByOneStep(): void
93+
public function onFailureAdvancesProgressByOneStep(): void
9194
{
9295
$exception = new Exception('foo');
9396
$uri = new Psr7\Uri('https://www.example.com');
@@ -96,10 +99,6 @@ public function onFailurePrintsFailedUrlAndAdvancesProgressBarByOneStep(): void
9699
$this->subject->onFailure($exception, $uri);
97100
$this->subject->onFailure($exception, $uri);
98101

99-
$output = $this->output->fetch();
100-
101-
self::assertNotEmpty($output);
102-
self::assertMatchesRegularExpression('#^\s*1/10 \S+\s+10% -- 1 failure#m', $output);
103-
self::assertMatchesRegularExpression('#^\s*2/10 \S+\s+20% -- 2 failures#m', $output);
102+
self::assertSame('FF', $this->output->fetch());
104103
}
105104
}

0 commit comments

Comments
 (0)