Skip to content

Commit cf9ef99

Browse files
committed
Fix output in agent tool use
1 parent 73dae21 commit cf9ef99

2 files changed

Lines changed: 171 additions & 2 deletions

File tree

src/Command/ErrorsConsoleStyle.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use OndraM\CiDetector\CiDetector;
66
use Override;
7+
use PHPStan\Internal\AgentDetector;
78
use Symfony\Component\Console\Helper\Helper;
89
use Symfony\Component\Console\Helper\ProgressBar;
910
use Symfony\Component\Console\Helper\TableSeparator;
@@ -95,9 +96,10 @@ public function createProgressBar(int $max = 0): ProgressBar
9596
}
9697

9798
$ci = $this->isCiDetected();
98-
$this->progressBar->setOverwrite(!$ci);
99+
$agent = AgentDetector::isRunningInAgent();
100+
$this->progressBar->setOverwrite(!$ci && !$agent);
99101

100-
if ($ci) {
102+
if ($ci || $agent) {
101103
$this->progressBar->minSecondsBetweenRedraws(15);
102104
$this->progressBar->maxSecondsBetweenRedraws(30);
103105
} elseif (DIRECTORY_SEPARATOR === '\\') {
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Command;
4+
5+
use Override;
6+
use PHPStan\Internal\AgentDetector;
7+
use PHPUnit\Framework\TestCase;
8+
use ReflectionClass;
9+
use Symfony\Component\Console\Helper\ProgressBar;
10+
use Symfony\Component\Console\Input\StringInput;
11+
use Symfony\Component\Console\Output\StreamOutput;
12+
use function fclose;
13+
use function fopen;
14+
use function getenv;
15+
use function putenv;
16+
use function rewind;
17+
use function rtrim;
18+
use function str_replace;
19+
use function stream_get_contents;
20+
use const PHP_VERSION_ID;
21+
22+
class ErrorsConsoleStyleTest extends TestCase
23+
{
24+
25+
/** @var array<string, string|false> */
26+
private array $originalEnvVars = [];
27+
28+
#[Override]
29+
protected function setUp(): void
30+
{
31+
foreach (AgentDetector::ENV_VARS as $var) {
32+
$this->originalEnvVars[$var] = getenv($var);
33+
putenv($var);
34+
}
35+
}
36+
37+
#[Override]
38+
protected function tearDown(): void
39+
{
40+
foreach (AgentDetector::ENV_VARS as $var) {
41+
putenv($var . ($this->originalEnvVars[$var] !== false ? '=' . $this->originalEnvVars[$var] : ''));
42+
}
43+
}
44+
45+
public function testCreateProgressBarTreatsAgentAsCiEnvironment(): void
46+
{
47+
putenv('AI_AGENT=1');
48+
49+
[$errorStyle, $stream] = $this->createErrorStyle();
50+
$this->setCiDetected($errorStyle, false);
51+
$progressBar = $errorStyle->createProgressBar(10);
52+
53+
self::assertFalse($this->getProgressBarBoolProperty($progressBar, 'overwrite'));
54+
self::assertSame(15.0, $this->getProgressBarFloatProperty($progressBar, 'minSecondsBetweenRedraws'));
55+
self::assertSame(30.0, $this->getProgressBarFloatProperty($progressBar, 'maxSecondsBetweenRedraws'));
56+
57+
fclose($stream);
58+
}
59+
60+
public function testCreateProgressBarOverwritesWhenOutsideCiAndAgent(): void
61+
{
62+
[$errorStyle, $stream] = $this->createErrorStyle();
63+
$this->setCiDetected($errorStyle, false);
64+
$progressBar = $errorStyle->createProgressBar(10);
65+
66+
self::assertTrue($this->getProgressBarBoolProperty($progressBar, 'overwrite'));
67+
self::assertLessThan(15.0, $this->getProgressBarFloatProperty($progressBar, 'maxSecondsBetweenRedraws'));
68+
69+
fclose($stream);
70+
}
71+
72+
public function testProgressOutputInAgentDoesNotOverwrite(): void
73+
{
74+
$agentOutput = $this->renderProgressOutput(true);
75+
$regularOutput = $this->renderProgressOutput(false);
76+
77+
self::assertSame(
78+
rtrim(<<<'EOT'
79+
0/2 [>---------------------------] 0%
80+
2/2 [============================] 100%
81+
EOT),
82+
$agentOutput,
83+
);
84+
self::assertSame(
85+
" 0/2 [>---------------------------] 0%\033[1G\033[2K 2/2 [============================] 100%",
86+
$regularOutput,
87+
);
88+
}
89+
90+
/**
91+
* @return array{ErrorsConsoleStyle, resource}
92+
*/
93+
private function createErrorStyle(): array
94+
{
95+
$stream = fopen('php://memory', 'w+');
96+
self::assertNotFalse($stream);
97+
98+
$output = new StreamOutput($stream, StreamOutput::VERBOSITY_NORMAL, true);
99+
$errorStyle = new ErrorsConsoleStyle(new StringInput(''), $output);
100+
101+
return [$errorStyle, $stream];
102+
}
103+
104+
private function renderProgressOutput(bool $isAgent): string
105+
{
106+
if ($isAgent) {
107+
putenv('AI_AGENT=1');
108+
} else {
109+
putenv('AI_AGENT');
110+
}
111+
112+
[$errorStyle, $stream] = $this->createErrorStyle();
113+
$this->setCiDetected($errorStyle, false);
114+
115+
$progressBar = $errorStyle->createProgressBar(2);
116+
$progressBar->setBarCharacter('=');
117+
$progressBar->setEmptyBarCharacter('-');
118+
$progressBar->setProgressCharacter('>');
119+
$progressBar->setProgress(0);
120+
$progressBar->display();
121+
$progressBar->setProgress(2);
122+
$progressBar->display();
123+
124+
rewind($stream);
125+
$output = stream_get_contents($stream);
126+
fclose($stream);
127+
128+
self::assertIsString($output);
129+
130+
return str_replace(["\r\n", "\r"], "\n", $output);
131+
}
132+
133+
private function setCiDetected(ErrorsConsoleStyle $errorStyle, bool $value): void
134+
{
135+
$reflectionClass = new ReflectionClass($errorStyle);
136+
$property = $reflectionClass->getProperty('isCiDetected');
137+
if (PHP_VERSION_ID < 80100) {
138+
$property->setAccessible(true); // @phpstan-ignore method.deprecated
139+
}
140+
$property->setValue($errorStyle, $value);
141+
}
142+
143+
private function getProgressBarBoolProperty(ProgressBar $progressBar, string $propertyName): bool
144+
{
145+
$reflectionClass = new ReflectionClass($progressBar);
146+
$property = $reflectionClass->getProperty($propertyName);
147+
if (PHP_VERSION_ID < 80100) {
148+
$property->setAccessible(true); // @phpstan-ignore method.deprecated
149+
}
150+
151+
/** @var bool */
152+
return $property->getValue($progressBar);
153+
}
154+
155+
private function getProgressBarFloatProperty(ProgressBar $progressBar, string $propertyName): float
156+
{
157+
$reflectionClass = new ReflectionClass($progressBar);
158+
$property = $reflectionClass->getProperty($propertyName);
159+
if (PHP_VERSION_ID < 80100) {
160+
$property->setAccessible(true); // @phpstan-ignore method.deprecated
161+
}
162+
163+
/** @var float */
164+
return $property->getValue($progressBar);
165+
}
166+
167+
}

0 commit comments

Comments
 (0)