Skip to content

Commit 40380e6

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

2 files changed

Lines changed: 157 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: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
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 stream_get_contents;
19+
20+
class ErrorsConsoleStyleTest extends TestCase
21+
{
22+
23+
/** @var array<string, string|false> */
24+
private array $originalEnvVars = [];
25+
26+
#[Override]
27+
protected function setUp(): void
28+
{
29+
foreach (AgentDetector::ENV_VARS as $var) {
30+
$this->originalEnvVars[$var] = getenv($var);
31+
putenv($var);
32+
}
33+
}
34+
35+
#[Override]
36+
protected function tearDown(): void
37+
{
38+
foreach (AgentDetector::ENV_VARS as $var) {
39+
putenv($var . ($this->originalEnvVars[$var] !== false ? '=' . $this->originalEnvVars[$var] : ''));
40+
}
41+
}
42+
43+
public function testCreateProgressBarTreatsAgentAsCiEnvironment(): void
44+
{
45+
putenv('AI_AGENT=1');
46+
47+
[$errorStyle, $stream] = $this->createErrorStyle();
48+
$this->setCiDetected($errorStyle, false);
49+
$progressBar = $errorStyle->createProgressBar(10);
50+
51+
self::assertFalse($this->getProgressBarBoolProperty($progressBar, 'overwrite'));
52+
self::assertSame(15.0, $this->getProgressBarFloatProperty($progressBar, 'minSecondsBetweenRedraws'));
53+
self::assertSame(30.0, $this->getProgressBarFloatProperty($progressBar, 'maxSecondsBetweenRedraws'));
54+
55+
fclose($stream);
56+
}
57+
58+
public function testCreateProgressBarOverwritesWhenOutsideCiAndAgent(): void
59+
{
60+
[$errorStyle, $stream] = $this->createErrorStyle();
61+
$this->setCiDetected($errorStyle, false);
62+
$progressBar = $errorStyle->createProgressBar(10);
63+
64+
self::assertTrue($this->getProgressBarBoolProperty($progressBar, 'overwrite'));
65+
self::assertLessThan(15.0, $this->getProgressBarFloatProperty($progressBar, 'maxSecondsBetweenRedraws'));
66+
67+
fclose($stream);
68+
}
69+
70+
public function testProgressOutputInAgentDoesNotOverwrite(): void
71+
{
72+
$agentOutput = $this->renderProgressOutput(true);
73+
$regularOutput = $this->renderProgressOutput(false);
74+
75+
self::assertSame(
76+
rtrim(<<<'EOT'
77+
0/2 [░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 0%
78+
2/2 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%
79+
EOT),
80+
$agentOutput,
81+
);
82+
self::assertSame(
83+
" 0/2 [░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 0%\033[1G\033[2K 2/2 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%",
84+
$regularOutput,
85+
);
86+
}
87+
88+
/**
89+
* @return array{ErrorsConsoleStyle, resource}
90+
*/
91+
private function createErrorStyle(): array
92+
{
93+
$stream = fopen('php://memory', 'w+');
94+
self::assertNotFalse($stream);
95+
96+
$output = new StreamOutput($stream, StreamOutput::VERBOSITY_NORMAL, true);
97+
$errorStyle = new ErrorsConsoleStyle(new StringInput(''), $output);
98+
99+
return [$errorStyle, $stream];
100+
}
101+
102+
private function renderProgressOutput(bool $isAgent): string
103+
{
104+
if ($isAgent) {
105+
putenv('AI_AGENT=1');
106+
} else {
107+
putenv('AI_AGENT');
108+
}
109+
110+
[$errorStyle, $stream] = $this->createErrorStyle();
111+
$this->setCiDetected($errorStyle, false);
112+
113+
$progressBar = $errorStyle->createProgressBar(2);
114+
$progressBar->setProgress(0);
115+
$progressBar->display();
116+
$progressBar->setProgress(2);
117+
$progressBar->display();
118+
119+
rewind($stream);
120+
$output = stream_get_contents($stream);
121+
fclose($stream);
122+
123+
self::assertIsString($output);
124+
125+
return $output;
126+
}
127+
128+
private function setCiDetected(ErrorsConsoleStyle $errorStyle, bool $value): void
129+
{
130+
$reflectionClass = new ReflectionClass($errorStyle);
131+
$property = $reflectionClass->getProperty('isCiDetected');
132+
$property->setValue($errorStyle, $value);
133+
}
134+
135+
private function getProgressBarBoolProperty(ProgressBar $progressBar, string $propertyName): bool
136+
{
137+
$reflectionClass = new ReflectionClass($progressBar);
138+
$property = $reflectionClass->getProperty($propertyName);
139+
140+
/** @var bool */
141+
return $property->getValue($progressBar);
142+
}
143+
144+
private function getProgressBarFloatProperty(ProgressBar $progressBar, string $propertyName): float
145+
{
146+
$reflectionClass = new ReflectionClass($progressBar);
147+
$property = $reflectionClass->getProperty($propertyName);
148+
149+
/** @var float */
150+
return $property->getValue($progressBar);
151+
}
152+
153+
}

0 commit comments

Comments
 (0)