ProgressBarTest.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Console\Tests\Helper;
  11. use PHPUnit\Framework\TestCase;
  12. use Symfony\Component\Console\Helper\Helper;
  13. use Symfony\Component\Console\Helper\ProgressBar;
  14. use Symfony\Component\Console\Output\StreamOutput;
  15. /**
  16. * @group time-sensitive
  17. */
  18. class ProgressBarTest extends TestCase
  19. {
  20. public function testMultipleStart()
  21. {
  22. $bar = new ProgressBar($output = $this->getOutputStream());
  23. $bar->start();
  24. $bar->advance();
  25. $bar->start();
  26. rewind($output->getStream());
  27. $this->assertEquals(
  28. ' 0 [>---------------------------]'.
  29. $this->generateOutput(' 1 [->--------------------------]').
  30. $this->generateOutput(' 0 [>---------------------------]'),
  31. stream_get_contents($output->getStream())
  32. );
  33. }
  34. public function testAdvance()
  35. {
  36. $bar = new ProgressBar($output = $this->getOutputStream());
  37. $bar->start();
  38. $bar->advance();
  39. rewind($output->getStream());
  40. $this->assertEquals(
  41. ' 0 [>---------------------------]'.
  42. $this->generateOutput(' 1 [->--------------------------]'),
  43. stream_get_contents($output->getStream())
  44. );
  45. }
  46. public function testAdvanceWithStep()
  47. {
  48. $bar = new ProgressBar($output = $this->getOutputStream());
  49. $bar->start();
  50. $bar->advance(5);
  51. rewind($output->getStream());
  52. $this->assertEquals(
  53. ' 0 [>---------------------------]'.
  54. $this->generateOutput(' 5 [----->----------------------]'),
  55. stream_get_contents($output->getStream())
  56. );
  57. }
  58. public function testAdvanceMultipleTimes()
  59. {
  60. $bar = new ProgressBar($output = $this->getOutputStream());
  61. $bar->start();
  62. $bar->advance(3);
  63. $bar->advance(2);
  64. rewind($output->getStream());
  65. $this->assertEquals(
  66. ' 0 [>---------------------------]'.
  67. $this->generateOutput(' 3 [--->------------------------]').
  68. $this->generateOutput(' 5 [----->----------------------]'),
  69. stream_get_contents($output->getStream())
  70. );
  71. }
  72. public function testAdvanceOverMax()
  73. {
  74. $bar = new ProgressBar($output = $this->getOutputStream(), 10);
  75. $bar->setProgress(9);
  76. $bar->advance();
  77. $bar->advance();
  78. rewind($output->getStream());
  79. $this->assertEquals(
  80. ' 9/10 [=========================>--] 90%'.
  81. $this->generateOutput(' 10/10 [============================] 100%').
  82. $this->generateOutput(' 11/11 [============================] 100%'),
  83. stream_get_contents($output->getStream())
  84. );
  85. }
  86. public function testFormat()
  87. {
  88. $expected =
  89. ' 0/10 [>---------------------------] 0%'.
  90. $this->generateOutput(' 10/10 [============================] 100%').
  91. $this->generateOutput(' 10/10 [============================] 100%')
  92. ;
  93. // max in construct, no format
  94. $bar = new ProgressBar($output = $this->getOutputStream(), 10);
  95. $bar->start();
  96. $bar->advance(10);
  97. $bar->finish();
  98. rewind($output->getStream());
  99. $this->assertEquals($expected, stream_get_contents($output->getStream()));
  100. // max in start, no format
  101. $bar = new ProgressBar($output = $this->getOutputStream());
  102. $bar->start(10);
  103. $bar->advance(10);
  104. $bar->finish();
  105. rewind($output->getStream());
  106. $this->assertEquals($expected, stream_get_contents($output->getStream()));
  107. // max in construct, explicit format before
  108. $bar = new ProgressBar($output = $this->getOutputStream(), 10);
  109. $bar->setFormat('normal');
  110. $bar->start();
  111. $bar->advance(10);
  112. $bar->finish();
  113. rewind($output->getStream());
  114. $this->assertEquals($expected, stream_get_contents($output->getStream()));
  115. // max in start, explicit format before
  116. $bar = new ProgressBar($output = $this->getOutputStream());
  117. $bar->setFormat('normal');
  118. $bar->start(10);
  119. $bar->advance(10);
  120. $bar->finish();
  121. rewind($output->getStream());
  122. $this->assertEquals($expected, stream_get_contents($output->getStream()));
  123. }
  124. public function testCustomizations()
  125. {
  126. $bar = new ProgressBar($output = $this->getOutputStream(), 10);
  127. $bar->setBarWidth(10);
  128. $bar->setBarCharacter('_');
  129. $bar->setEmptyBarCharacter(' ');
  130. $bar->setProgressCharacter('/');
  131. $bar->setFormat(' %current%/%max% [%bar%] %percent:3s%%');
  132. $bar->start();
  133. $bar->advance();
  134. rewind($output->getStream());
  135. $this->assertEquals(
  136. ' 0/10 [/ ] 0%'.
  137. $this->generateOutput(' 1/10 [_/ ] 10%'),
  138. stream_get_contents($output->getStream())
  139. );
  140. }
  141. public function testDisplayWithoutStart()
  142. {
  143. $bar = new ProgressBar($output = $this->getOutputStream(), 50);
  144. $bar->display();
  145. rewind($output->getStream());
  146. $this->assertEquals(
  147. ' 0/50 [>---------------------------] 0%',
  148. stream_get_contents($output->getStream())
  149. );
  150. }
  151. public function testDisplayWithQuietVerbosity()
  152. {
  153. $bar = new ProgressBar($output = $this->getOutputStream(true, StreamOutput::VERBOSITY_QUIET), 50);
  154. $bar->display();
  155. rewind($output->getStream());
  156. $this->assertEquals(
  157. '',
  158. stream_get_contents($output->getStream())
  159. );
  160. }
  161. public function testFinishWithoutStart()
  162. {
  163. $bar = new ProgressBar($output = $this->getOutputStream(), 50);
  164. $bar->finish();
  165. rewind($output->getStream());
  166. $this->assertEquals(
  167. ' 50/50 [============================] 100%',
  168. stream_get_contents($output->getStream())
  169. );
  170. }
  171. public function testPercent()
  172. {
  173. $bar = new ProgressBar($output = $this->getOutputStream(), 50);
  174. $bar->start();
  175. $bar->display();
  176. $bar->advance();
  177. $bar->advance();
  178. rewind($output->getStream());
  179. $this->assertEquals(
  180. ' 0/50 [>---------------------------] 0%'.
  181. $this->generateOutput(' 0/50 [>---------------------------] 0%').
  182. $this->generateOutput(' 1/50 [>---------------------------] 2%').
  183. $this->generateOutput(' 2/50 [=>--------------------------] 4%'),
  184. stream_get_contents($output->getStream())
  185. );
  186. }
  187. public function testOverwriteWithShorterLine()
  188. {
  189. $bar = new ProgressBar($output = $this->getOutputStream(), 50);
  190. $bar->setFormat(' %current%/%max% [%bar%] %percent:3s%%');
  191. $bar->start();
  192. $bar->display();
  193. $bar->advance();
  194. // set shorter format
  195. $bar->setFormat(' %current%/%max% [%bar%]');
  196. $bar->advance();
  197. rewind($output->getStream());
  198. $this->assertEquals(
  199. ' 0/50 [>---------------------------] 0%'.
  200. $this->generateOutput(' 0/50 [>---------------------------] 0%').
  201. $this->generateOutput(' 1/50 [>---------------------------] 2%').
  202. $this->generateOutput(' 2/50 [=>--------------------------]'),
  203. stream_get_contents($output->getStream())
  204. );
  205. }
  206. public function testStartWithMax()
  207. {
  208. $bar = new ProgressBar($output = $this->getOutputStream());
  209. $bar->setFormat('%current%/%max% [%bar%]');
  210. $bar->start(50);
  211. $bar->advance();
  212. rewind($output->getStream());
  213. $this->assertEquals(
  214. ' 0/50 [>---------------------------]'.
  215. $this->generateOutput(' 1/50 [>---------------------------]'),
  216. stream_get_contents($output->getStream())
  217. );
  218. }
  219. public function testSetCurrentProgress()
  220. {
  221. $bar = new ProgressBar($output = $this->getOutputStream(), 50);
  222. $bar->start();
  223. $bar->display();
  224. $bar->advance();
  225. $bar->setProgress(15);
  226. $bar->setProgress(25);
  227. rewind($output->getStream());
  228. $this->assertEquals(
  229. ' 0/50 [>---------------------------] 0%'.
  230. $this->generateOutput(' 0/50 [>---------------------------] 0%').
  231. $this->generateOutput(' 1/50 [>---------------------------] 2%').
  232. $this->generateOutput(' 15/50 [========>-------------------] 30%').
  233. $this->generateOutput(' 25/50 [==============>-------------] 50%'),
  234. stream_get_contents($output->getStream())
  235. );
  236. }
  237. public function testSetCurrentBeforeStarting()
  238. {
  239. $bar = new ProgressBar($this->getOutputStream());
  240. $bar->setProgress(15);
  241. $this->assertNotNull($bar->getStartTime());
  242. }
  243. /**
  244. * @expectedException \LogicException
  245. * @expectedExceptionMessage You can't regress the progress bar
  246. */
  247. public function testRegressProgress()
  248. {
  249. $bar = new ProgressBar($output = $this->getOutputStream(), 50);
  250. $bar->start();
  251. $bar->setProgress(15);
  252. $bar->setProgress(10);
  253. }
  254. public function testRedrawFrequency()
  255. {
  256. $bar = $this->getMockBuilder('Symfony\Component\Console\Helper\ProgressBar')->setMethods(array('display'))->setConstructorArgs(array($this->getOutputStream(), 6))->getMock();
  257. $bar->expects($this->exactly(4))->method('display');
  258. $bar->setRedrawFrequency(2);
  259. $bar->start();
  260. $bar->setProgress(1);
  261. $bar->advance(2);
  262. $bar->advance(2);
  263. $bar->advance(1);
  264. }
  265. public function testRedrawFrequencyIsAtLeastOneIfZeroGiven()
  266. {
  267. $bar = $this->getMockBuilder('Symfony\Component\Console\Helper\ProgressBar')->setMethods(array('display'))->setConstructorArgs(array($this->getOutputStream()))->getMock();
  268. $bar->expects($this->exactly(2))->method('display');
  269. $bar->setRedrawFrequency(0);
  270. $bar->start();
  271. $bar->advance();
  272. }
  273. public function testRedrawFrequencyIsAtLeastOneIfSmallerOneGiven()
  274. {
  275. $bar = $this->getMockBuilder('Symfony\Component\Console\Helper\ProgressBar')->setMethods(array('display'))->setConstructorArgs(array($this->getOutputStream()))->getMock();
  276. $bar->expects($this->exactly(2))->method('display');
  277. $bar->setRedrawFrequency(0.9);
  278. $bar->start();
  279. $bar->advance();
  280. }
  281. public function testMultiByteSupport()
  282. {
  283. $bar = new ProgressBar($output = $this->getOutputStream());
  284. $bar->start();
  285. $bar->setBarCharacter('■');
  286. $bar->advance(3);
  287. rewind($output->getStream());
  288. $this->assertEquals(
  289. ' 0 [>---------------------------]'.
  290. $this->generateOutput(' 3 [■■■>------------------------]'),
  291. stream_get_contents($output->getStream())
  292. );
  293. }
  294. public function testClear()
  295. {
  296. $bar = new ProgressBar($output = $this->getOutputStream(), 50);
  297. $bar->start();
  298. $bar->setProgress(25);
  299. $bar->clear();
  300. rewind($output->getStream());
  301. $this->assertEquals(
  302. ' 0/50 [>---------------------------] 0%'.
  303. $this->generateOutput(' 25/50 [==============>-------------] 50%').
  304. $this->generateOutput(''),
  305. stream_get_contents($output->getStream())
  306. );
  307. }
  308. public function testPercentNotHundredBeforeComplete()
  309. {
  310. $bar = new ProgressBar($output = $this->getOutputStream(), 200);
  311. $bar->start();
  312. $bar->display();
  313. $bar->advance(199);
  314. $bar->advance();
  315. rewind($output->getStream());
  316. $this->assertEquals(
  317. ' 0/200 [>---------------------------] 0%'.
  318. $this->generateOutput(' 0/200 [>---------------------------] 0%').
  319. $this->generateOutput(' 199/200 [===========================>] 99%').
  320. $this->generateOutput(' 200/200 [============================] 100%'),
  321. stream_get_contents($output->getStream())
  322. );
  323. }
  324. public function testNonDecoratedOutput()
  325. {
  326. $bar = new ProgressBar($output = $this->getOutputStream(false), 200);
  327. $bar->start();
  328. for ($i = 0; $i < 200; ++$i) {
  329. $bar->advance();
  330. }
  331. $bar->finish();
  332. rewind($output->getStream());
  333. $this->assertEquals(
  334. ' 0/200 [>---------------------------] 0%'.PHP_EOL.
  335. ' 20/200 [==>-------------------------] 10%'.PHP_EOL.
  336. ' 40/200 [=====>----------------------] 20%'.PHP_EOL.
  337. ' 60/200 [========>-------------------] 30%'.PHP_EOL.
  338. ' 80/200 [===========>----------------] 40%'.PHP_EOL.
  339. ' 100/200 [==============>-------------] 50%'.PHP_EOL.
  340. ' 120/200 [================>-----------] 60%'.PHP_EOL.
  341. ' 140/200 [===================>--------] 70%'.PHP_EOL.
  342. ' 160/200 [======================>-----] 80%'.PHP_EOL.
  343. ' 180/200 [=========================>--] 90%'.PHP_EOL.
  344. ' 200/200 [============================] 100%',
  345. stream_get_contents($output->getStream())
  346. );
  347. }
  348. public function testNonDecoratedOutputWithClear()
  349. {
  350. $bar = new ProgressBar($output = $this->getOutputStream(false), 50);
  351. $bar->start();
  352. $bar->setProgress(25);
  353. $bar->clear();
  354. $bar->setProgress(50);
  355. $bar->finish();
  356. rewind($output->getStream());
  357. $this->assertEquals(
  358. ' 0/50 [>---------------------------] 0%'.PHP_EOL.
  359. ' 25/50 [==============>-------------] 50%'.PHP_EOL.
  360. ' 50/50 [============================] 100%',
  361. stream_get_contents($output->getStream())
  362. );
  363. }
  364. public function testNonDecoratedOutputWithoutMax()
  365. {
  366. $bar = new ProgressBar($output = $this->getOutputStream(false));
  367. $bar->start();
  368. $bar->advance();
  369. rewind($output->getStream());
  370. $this->assertEquals(
  371. ' 0 [>---------------------------]'.PHP_EOL.
  372. ' 1 [->--------------------------]',
  373. stream_get_contents($output->getStream())
  374. );
  375. }
  376. public function testParallelBars()
  377. {
  378. $output = $this->getOutputStream();
  379. $bar1 = new ProgressBar($output, 2);
  380. $bar2 = new ProgressBar($output, 3);
  381. $bar2->setProgressCharacter('#');
  382. $bar3 = new ProgressBar($output);
  383. $bar1->start();
  384. $output->write("\n");
  385. $bar2->start();
  386. $output->write("\n");
  387. $bar3->start();
  388. for ($i = 1; $i <= 3; ++$i) {
  389. // up two lines
  390. $output->write("\033[2A");
  391. if ($i <= 2) {
  392. $bar1->advance();
  393. }
  394. $output->write("\n");
  395. $bar2->advance();
  396. $output->write("\n");
  397. $bar3->advance();
  398. }
  399. $output->write("\033[2A");
  400. $output->write("\n");
  401. $output->write("\n");
  402. $bar3->finish();
  403. rewind($output->getStream());
  404. $this->assertEquals(
  405. ' 0/2 [>---------------------------] 0%'."\n".
  406. ' 0/3 [#---------------------------] 0%'."\n".
  407. rtrim(' 0 [>---------------------------]').
  408. "\033[2A".
  409. $this->generateOutput(' 1/2 [==============>-------------] 50%')."\n".
  410. $this->generateOutput(' 1/3 [=========#------------------] 33%')."\n".
  411. rtrim($this->generateOutput(' 1 [->--------------------------]')).
  412. "\033[2A".
  413. $this->generateOutput(' 2/2 [============================] 100%')."\n".
  414. $this->generateOutput(' 2/3 [==================#---------] 66%')."\n".
  415. rtrim($this->generateOutput(' 2 [-->-------------------------]')).
  416. "\033[2A".
  417. "\n".
  418. $this->generateOutput(' 3/3 [============================] 100%')."\n".
  419. rtrim($this->generateOutput(' 3 [--->------------------------]')).
  420. "\033[2A".
  421. "\n".
  422. "\n".
  423. rtrim($this->generateOutput(' 3 [============================]')),
  424. stream_get_contents($output->getStream())
  425. );
  426. }
  427. public function testWithoutMax()
  428. {
  429. $output = $this->getOutputStream();
  430. $bar = new ProgressBar($output);
  431. $bar->start();
  432. $bar->advance();
  433. $bar->advance();
  434. $bar->advance();
  435. $bar->finish();
  436. rewind($output->getStream());
  437. $this->assertEquals(
  438. rtrim(' 0 [>---------------------------]').
  439. rtrim($this->generateOutput(' 1 [->--------------------------]')).
  440. rtrim($this->generateOutput(' 2 [-->-------------------------]')).
  441. rtrim($this->generateOutput(' 3 [--->------------------------]')).
  442. rtrim($this->generateOutput(' 3 [============================]')),
  443. stream_get_contents($output->getStream())
  444. );
  445. }
  446. public function testAddingPlaceholderFormatter()
  447. {
  448. ProgressBar::setPlaceholderFormatterDefinition('remaining_steps', function (ProgressBar $bar) {
  449. return $bar->getMaxSteps() - $bar->getProgress();
  450. });
  451. $bar = new ProgressBar($output = $this->getOutputStream(), 3);
  452. $bar->setFormat(' %remaining_steps% [%bar%]');
  453. $bar->start();
  454. $bar->advance();
  455. $bar->finish();
  456. rewind($output->getStream());
  457. $this->assertEquals(
  458. ' 3 [>---------------------------]'.
  459. $this->generateOutput(' 2 [=========>------------------]').
  460. $this->generateOutput(' 0 [============================]'),
  461. stream_get_contents($output->getStream())
  462. );
  463. }
  464. public function testMultilineFormat()
  465. {
  466. $bar = new ProgressBar($output = $this->getOutputStream(), 3);
  467. $bar->setFormat("%bar%\nfoobar");
  468. $bar->start();
  469. $bar->advance();
  470. $bar->clear();
  471. $bar->finish();
  472. rewind($output->getStream());
  473. $this->assertEquals(
  474. ">---------------------------\nfoobar".
  475. $this->generateOutput("=========>------------------\nfoobar").
  476. "\x0D\x1B[2K\x1B[1A\x1B[2K".
  477. $this->generateOutput("============================\nfoobar"),
  478. stream_get_contents($output->getStream())
  479. );
  480. }
  481. public function testAnsiColorsAndEmojis()
  482. {
  483. $bar = new ProgressBar($output = $this->getOutputStream(), 15);
  484. ProgressBar::setPlaceholderFormatterDefinition('memory', function (ProgressBar $bar) {
  485. static $i = 0;
  486. $mem = 100000 * $i;
  487. $colors = $i++ ? '41;37' : '44;37';
  488. return "\033[".$colors.'m '.Helper::formatMemory($mem)." \033[0m";
  489. });
  490. $bar->setFormat(" \033[44;37m %title:-37s% \033[0m\n %current%/%max% %bar% %percent:3s%%\n 🏁 %remaining:-10s% %memory:37s%");
  491. $bar->setBarCharacter($done = "\033[32m●\033[0m");
  492. $bar->setEmptyBarCharacter($empty = "\033[31m●\033[0m");
  493. $bar->setProgressCharacter($progress = "\033[32m➤ \033[0m");
  494. $bar->setMessage('Starting the demo... fingers crossed', 'title');
  495. $bar->start();
  496. $bar->setMessage('Looks good to me...', 'title');
  497. $bar->advance(4);
  498. $bar->setMessage('Thanks, bye', 'title');
  499. $bar->finish();
  500. rewind($output->getStream());
  501. $this->assertEquals(
  502. " \033[44;37m Starting the demo... fingers crossed \033[0m\n".
  503. ' 0/15 '.$progress.str_repeat($empty, 26)." 0%\n".
  504. " \xf0\x9f\x8f\x81 < 1 sec \033[44;37m 0 B \033[0m"
  505. .
  506. $this->generateOutput(
  507. " \033[44;37m Looks good to me... \033[0m\n".
  508. ' 4/15 '.str_repeat($done, 7).$progress.str_repeat($empty, 19)." 26%\n".
  509. " \xf0\x9f\x8f\x81 < 1 sec \033[41;37m 97 KiB \033[0m"
  510. ).
  511. $this->generateOutput(
  512. " \033[44;37m Thanks, bye \033[0m\n".
  513. ' 15/15 '.str_repeat($done, 28)." 100%\n".
  514. " \xf0\x9f\x8f\x81 < 1 sec \033[41;37m 195 KiB \033[0m"
  515. ),
  516. stream_get_contents($output->getStream())
  517. );
  518. }
  519. public function testSetFormat()
  520. {
  521. $bar = new ProgressBar($output = $this->getOutputStream());
  522. $bar->setFormat('normal');
  523. $bar->start();
  524. rewind($output->getStream());
  525. $this->assertEquals(
  526. ' 0 [>---------------------------]',
  527. stream_get_contents($output->getStream())
  528. );
  529. $bar = new ProgressBar($output = $this->getOutputStream(), 10);
  530. $bar->setFormat('normal');
  531. $bar->start();
  532. rewind($output->getStream());
  533. $this->assertEquals(
  534. ' 0/10 [>---------------------------] 0%',
  535. stream_get_contents($output->getStream())
  536. );
  537. }
  538. /**
  539. * @dataProvider provideFormat
  540. */
  541. public function testFormatsWithoutMax($format)
  542. {
  543. $bar = new ProgressBar($output = $this->getOutputStream());
  544. $bar->setFormat($format);
  545. $bar->start();
  546. rewind($output->getStream());
  547. $this->assertNotEmpty(stream_get_contents($output->getStream()));
  548. }
  549. /**
  550. * Provides each defined format.
  551. *
  552. * @return array
  553. */
  554. public function provideFormat()
  555. {
  556. return array(
  557. array('normal'),
  558. array('verbose'),
  559. array('very_verbose'),
  560. array('debug'),
  561. );
  562. }
  563. protected function getOutputStream($decorated = true, $verbosity = StreamOutput::VERBOSITY_NORMAL)
  564. {
  565. return new StreamOutput(fopen('php://memory', 'r+', false), $verbosity, $decorated);
  566. }
  567. protected function generateOutput($expected)
  568. {
  569. $count = substr_count($expected, "\n");
  570. return "\x0D\x1B[2K".($count ? str_repeat("\x1B[1A\x1B[2K", $count) : '').$expected;
  571. }
  572. }